0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.openide.windows;
0043:
0044: import java.awt.EventQueue;
0045: import java.awt.Image;
0046: import java.awt.Toolkit;
0047: import java.awt.event.ActionEvent;
0048: import java.awt.event.ActionListener;
0049: import java.awt.event.KeyEvent;
0050: import java.beans.PropertyChangeEvent;
0051: import java.beans.PropertyChangeListener;
0052: import java.io.Externalizable;
0053: import java.io.IOException;
0054: import java.io.ObjectInput;
0055: import java.io.ObjectInputStream;
0056: import java.io.ObjectOutput;
0057: import java.io.ObjectOutputStream;
0058: import java.io.ObjectStreamException;
0059: import java.io.Serializable;
0060: import java.lang.ref.Reference;
0061: import java.lang.ref.WeakReference;
0062: import java.lang.reflect.InvocationTargetException;
0063: import java.lang.reflect.Method;
0064: import java.util.ArrayList;
0065: import java.util.Arrays;
0066: import java.util.Collection;
0067: import java.util.List;
0068: import java.util.Set;
0069: import java.util.logging.Level;
0070: import java.util.logging.LogRecord;
0071: import java.util.logging.Logger;
0072: import javax.accessibility.Accessible;
0073: import javax.accessibility.AccessibleContext;
0074: import javax.accessibility.AccessibleRole;
0075: import javax.swing.AbstractAction;
0076: import javax.swing.Action;
0077: import javax.swing.ActionMap;
0078: import javax.swing.JComponent;
0079: import javax.swing.KeyStroke;
0080: import javax.swing.SwingUtilities;
0081: import javax.swing.Timer;
0082: import javax.swing.plaf.basic.BasicHTML;
0083: import javax.swing.text.Keymap;
0084: import org.openide.awt.UndoRedo;
0085: import org.openide.nodes.Node;
0086: import org.openide.nodes.NodeAdapter;
0087: import org.openide.nodes.NodeListener;
0088: import org.openide.util.ContextAwareAction;
0089: import org.openide.util.HelpCtx;
0090: import org.openide.util.Lookup;
0091: import org.openide.util.NbBundle;
0092: import org.openide.util.Utilities;
0093: import org.openide.util.WeakListeners;
0094: import org.openide.util.WeakSet;
0095: import org.openide.util.actions.SystemAction;
0096:
0097: /**
0098: * Embeddable visual component to be displayed in NetBeans.
0099: * This is the basic unit of display--windows should not be
0100: * created directly, but rather use this class.
0101: * A top component may correspond to a single window, but may also
0102: * be a tab (e.g.) in a window. It may be docked or undocked,
0103: * have selected nodes, supply actions, etc.
0104: *
0105: * Important serialization note: Serialization of this TopComponent is designed
0106: * in a way that it's not desired to override writeReplace method. If you would
0107: * like to resolve to something, please implement readResolve() method directly
0108: * on your top component.
0109: *
0110: * @author Jaroslav Tulach, Petr Hamernik, Jan Jancura
0111: */
0112: public class TopComponent extends JComponent implements Externalizable,
0113: Accessible, HelpCtx.Provider, Lookup.Provider {
0114: /** UI logger to notify about invocation of an action */
0115: private static Logger UILOG = Logger
0116: .getLogger("org.netbeans.ui.actions"); // NOI18N
0117: /** generated Serialized Version UID */
0118: static final long serialVersionUID = -3022538025284122942L;
0119: /** top component logger */
0120: static final Logger LOG = Logger.getLogger(TopComponent.class
0121: .getName());
0122:
0123: /** Behavior in which a top component closed (by the user) in one workspace
0124: * will be removed from <em>every</em> workspace.
0125: * Also, {@link #close} is called.
0126: * This is appropriate for top components such as Editor panes which
0127: * the user expects to really close (and prompt to save) when closed
0128: * in any
0129: * @deprecated Do not use. It is redundant since workspaces are not supported anymore. */
0130: @Deprecated
0131: public static final int CLOSE_EACH = 0;
0132:
0133: /** Behavior in which a top component closed (by the user) in one workspace
0134: * may be left in other workspaces.
0135: * Only when the last remaining manifestation in any workspace is closed
0136: * will the object be deleted using {@link #close}.
0137: * Appropriate for components containing no user data, for which closing
0138: * the component is only likely to result from the user's wanting to remove
0139: * it from active view (on the current workspace).
0140: * @deprecated Do not use. It is redundant since workspaces are not supported anymore. */
0141: @Deprecated
0142: public static final int CLOSE_LAST = 1;
0143:
0144: /** Persistence type of TopComponent instance. TopComponent is persistent. */
0145: public static final int PERSISTENCE_ALWAYS = 0;
0146:
0147: /** Persistence type of TopComponent instance. TopComponent is persistent only when
0148: * it is opened in Mode. */
0149: public static final int PERSISTENCE_ONLY_OPENED = 1;
0150:
0151: /** Persistence type of TopComponent instance. TopComponent is not persistent. */
0152: public static final int PERSISTENCE_NEVER = 2;
0153:
0154: /** a lock for operations in default impl of getLookup */
0155: private static Object defaultLookupLock = new Object();
0156:
0157: /** Classes that have been warned about overriding preferredID() */
0158: private static final Set<Class> warnedTCPIClasses = new WeakSet<Class>();
0159:
0160: /** Used to print warning about getPersistenceType */
0161: private static final Set<Class> warnedClasses = new WeakSet<Class>();
0162:
0163: /** reference to Lookup with default implementation for the
0164: * component or the lookup associated with the component itself
0165: */
0166: private Object defaultLookupRef;
0167:
0168: /** Holds support for sync with node display name or null */
0169: private NodeName nodeName;
0170:
0171: // Do not use, deprecated.
0172:
0173: /** constant for desired close operation */
0174: private int closeOperation = CLOSE_LAST;
0175:
0176: /** Icon of this <code>TopComponent</code> */
0177: private transient Image icon;
0178:
0179: /** Activated nodes of this <code>TopComponent</code>. */
0180: private transient Node[] activatedNodes;
0181:
0182: /** Localized display name of this <code>TopComponent</code>. */
0183: private transient String displayName;
0184:
0185: /** Holds localized display name of this <code>TopComponent</code> in html syntax,
0186: * or null if not needed */
0187: private String htmlDisplayName;
0188:
0189: /** identification of serialization version
0190: * Used in CloneableTopComponent readObject method.
0191: */
0192: short serialVersion = 1;
0193: private AttentionGetter attentionGetter = null;
0194:
0195: /** Create a top component.
0196: */
0197: public TopComponent() {
0198: this ((Lookup) null);
0199: }
0200:
0201: /** Creates a top component for a provided lookup that will delegate
0202: * take and synchronize activated nodes and ActionMap from a provided
0203: * lookup. The lookup will also be returned from {@link #getLookup} method,
0204: * if not overriden.
0205: *
0206: * @param lookup the lookup to associate with
0207: * @since 4.19
0208: */
0209: public TopComponent(Lookup lookup) {
0210: if (lookup != null) {
0211: setLookup(lookup, true);
0212: }
0213:
0214: enableEvents(java.awt.AWTEvent.KEY_EVENT_MASK);
0215:
0216: // #27731 TopComponent itself shouldn't get the focus.
0217: // XXX What to do in case nothing in TopComponent is focusable?
0218: setFocusable(false);
0219: initActionMap(lookup);
0220: }
0221:
0222: // It is necessary so the old actions (clone and close from org.openide.actions package) remain working.
0223:
0224: /** Initialized <code>ActionMap</code> of this <code>TopComponent</code>.
0225: * @since 4.13 */
0226: private void initActionMap(Lookup lookup) {
0227: ActionMap inner = null;
0228: if (lookup != null) {
0229: inner = lookup.lookup(ActionMap.class);
0230: }
0231: if (inner == null) {
0232: inner = new ActionMap();
0233: }
0234:
0235: DelegateActionMap am = new DelegateActionMap(this , inner);
0236:
0237: if (this instanceof TopComponent.Cloneable) {
0238: am.put("cloneWindow", // NOI18N
0239: new CloneWindowAction(am));
0240: }
0241:
0242: am.put("closeWindow", // NOI18N
0243: new CloseWindowAction(am));
0244:
0245: setActionMap(am);
0246: }
0247:
0248: /** Getter for class that allows obtaining of information about components.
0249: * It allows to find out which component is selected, which nodes are
0250: * currently or has been activated and list of all components.
0251: *
0252: * @return the registry of components
0253: */
0254: public static final Registry getRegistry() {
0255: return WindowManager.getDefault().getRegistry();
0256: }
0257:
0258: /** Get the set of activated nodes in this component.
0259: * @return the activated nodes for this component or <code>null</code>, <code>null</code>
0260: * means such component does not change {@link Registry#getActivatedNodes()} just
0261: * {@link Registry#getCurrentNodes()} when this component gets activated */
0262: public final Node[] getActivatedNodes() {
0263: return activatedNodes;
0264: }
0265:
0266: /** Set the set of activated nodes in this component.
0267: * @param activatedNodes activated nodes for this component
0268: */
0269: public final void setActivatedNodes(Node[] activatedNodes) {
0270: assert multiviewActivatedNodes();
0271: setActivatedNodesImpl(activatedNodes);
0272: }
0273:
0274: private boolean multiviewActivatedNodes() {
0275: if ("org.netbeans.core.multiview.MultiViewTopComponent"
0276: .equals(this .getClass().getName())
0277: || //NOI18N
0278: "org.netbeans.core.multiview.MultiViewCloneableTopComponent"
0279: .equals(this .getClass().getName())) { //NOI18N
0280: LOG
0281: .info("Warning: You should not call setActivatedNodes()"
0282: + //NOI18N
0283: " on the multiview topcomponents. Instead please manipulate the lookup of the embedded MultiViewElements."
0284: + //NOI18N
0285: " For details, please see http://www.netbeans.org/issues/show_bug.cgi?id=67257");//NOI18N
0286: }
0287: return true;
0288: }
0289:
0290: private void setActivatedNodesImpl(Node[] activatedNodes) {
0291: boolean l = LOG.isLoggable(Level.FINER);
0292:
0293: if (Arrays.equals(this .activatedNodes, activatedNodes)) {
0294: if (l) {
0295: LOG.finer("No change to activatedNodes for " + this ); // NOI18N
0296: }
0297: return;
0298: }
0299:
0300: Lookup lookup = getLookup(false);
0301:
0302: if (lookup instanceof DefaultTopComponentLookup) {
0303: if (l) {
0304: LOG.finer("Updating lookup " + lookup + " for " + this ); // NOI18N
0305: }
0306: ((DefaultTopComponentLookup) lookup)
0307: .updateLookups(activatedNodes);
0308: }
0309:
0310: Node[] old = this .activatedNodes;
0311: this .activatedNodes = activatedNodes;
0312:
0313: if (l) {
0314: LOG.finer("activatedNodes changed: "
0315: + (activatedNodes == null ? "" : Arrays.asList(
0316: activatedNodes).toString())); // NOI18N
0317: }
0318: // notify all that are interested...
0319: WindowManager.getDefault().topComponentActivatedNodesChanged(
0320: this , this .activatedNodes);
0321:
0322: if (l) {
0323: LOG.finer("window manager notified: " + this ); // NOI18N
0324: }
0325:
0326: firePropertyChange("activatedNodes", old, this .activatedNodes); // NOI18N
0327:
0328: if (l) {
0329: LOG.finer("listeners notified: " + this ); // NOI18N
0330: }
0331: }
0332:
0333: /**
0334: * Overwrite when you want to change default persistence type. Default
0335: * persistence type is PERSISTENCE_ALWAYS.
0336: * Return value should be constant over a given TC's lifetime.
0337: * @return one of P_X constants
0338: * @since 4.20
0339: */
0340: public int getPersistenceType() {
0341: //First check for 'PersistenceType' client property for compatibility.
0342: if (warnedClasses.add(getClass())
0343: && !TopComponent.class.equals(getClass())) {
0344: Logger
0345: .getAnonymousLogger()
0346: .warning(
0347: "Note - " // NOI18N
0348: + getClass().getName()
0349: + " ought to override getPersistenceType()" // NOI18N
0350: + " rather than using the client property or accepting the default."); // NOI18N
0351: }
0352:
0353: String propValue = (String) getClientProperty("PersistenceType"); // NOI18N
0354:
0355: if (propValue == null) {
0356: return PERSISTENCE_ALWAYS;
0357: } else if ("Never".equals(propValue)) { // NOI18N
0358:
0359: return PERSISTENCE_NEVER;
0360: } else if ("OnlyOpened".equals(propValue)) { // NOI18N
0361:
0362: return PERSISTENCE_ONLY_OPENED;
0363: } else {
0364: return PERSISTENCE_ALWAYS;
0365: }
0366: }
0367:
0368: /** Get the undo/redo support for this component.
0369: * The default implementation returns a dummy support that cannot
0370: * undo anything.
0371: *
0372: * @return undoable edit for this component
0373: */
0374: public UndoRedo getUndoRedo() {
0375: return UndoRedo.NONE;
0376: }
0377:
0378: /** Shows this <code>TopComponent</code>.
0379: * <em>Note:</em> This method only makes it visible, but does not
0380: * activates it.
0381: * @see #requestActive */
0382: public void open() {
0383: open(null);
0384: }
0385:
0386: /** Shows this <code>TopComponent</code> in current workspace.
0387: * <em>Node:</em> Currently workspaces are not supported. The method has the same effect
0388: * like {@link #open()}.
0389: * @deprecated Use {@link #open()} instead. */
0390: @Deprecated
0391: public void open(Workspace workspace) {
0392: WindowManager.getDefault().topComponentOpen(this );
0393: }
0394:
0395: /** Opens TopComponent at given position in the mode. TopComponent is inserted at given
0396: * position, positions of already opened TopComponents in the same mode are
0397: * incremented.
0398: *
0399: * <ul>
0400: * <li>Does no operation if this TopComponent is already opened.</li>
0401: * <li>For position value less then 0, TopComponent is opened at position 0, the very first one.</li>
0402: * <li>For position value greater then count of opened TopComponents in the mode,
0403: * TopComponent is opened at last position</li>
0404: * </ul>
0405: *
0406: * @param position Index of the requested position.
0407: * @since 6.15
0408: */
0409: public final void openAtTabPosition(int position) {
0410: WindowManager.getDefault().topComponentOpenAtTabPosition(this ,
0411: position);
0412: }
0413:
0414: /** Gives position index of opened TopComponent in the mode.
0415: *
0416: * For closed TopComponents, position value less then zero is returned.
0417: *
0418: * @return Index of position.
0419: * @since 6.15
0420: */
0421: public final int getTabPosition() {
0422: return WindowManager.getDefault().topComponentGetTabPosition(
0423: this );
0424: }
0425:
0426: /** Indicates whether this <code>TopComponent</code> is opened.
0427: * @return true if given top component is opened, false otherwise */
0428: public final boolean isOpened() {
0429: return isOpened(null);
0430: }
0431:
0432: /** Indicates whether this <code>TopComponent</code> is opened in current workspace.
0433: * <em>Node:</em> Currently workspaces are not supported. The method has the same effect
0434: * like {@link #isOpened()}.
0435: * @deprecated Use {@link #isOpened()} instead. */
0436: @Deprecated
0437: public final boolean isOpened(Workspace workspace) {
0438: return WindowManager.getDefault().topComponentIsOpened(this );
0439: }
0440:
0441: /** Closes this <code>TopComponent</code>.
0442: * @return true if top component was succesfully closed, false if
0443: * top component for some reason refused to close. */
0444: public final boolean close() {
0445: return close(null);
0446: }
0447:
0448: /** Closes this <code>TopComponent</code> in current workspace.
0449: * <em>Node:</em> Currently workspaces are not supported. The method has the same effect
0450: * like {@link #close()}.
0451: * @deprecated Use {@link #close()} instead. */
0452: @Deprecated
0453: public final boolean close(Workspace workspace) {
0454: if (!isOpened()) {
0455: return true;
0456: }
0457:
0458: if (canClose()) {
0459: WindowManager.getDefault().topComponentClose(this );
0460:
0461: return true;
0462: } else {
0463: return false;
0464: }
0465: }
0466:
0467: /** This method is called when this <code>TopComponent</code> is about to close.
0468: * Allows subclasses to decide if <code>TopComponent</code> is ready to close.
0469: * @since 4.13 */
0470: public boolean canClose() {
0471: if (!isOpened()) {
0472: return false;
0473: }
0474:
0475: return canClose(null, true);
0476: }
0477:
0478: /** This method is called when top component is about to close.
0479: * Allows subclasses to decide if top component is ready for closing
0480: * or not.<br>
0481: * Default implementation always return true.
0482: *
0483: * @param workspace the workspace on which we are about to close or
0484: * null which means that component will be closed
0485: * on all workspaces where it is opened (CLOSE_EACH mode)
0486: * @param last true if this is last workspace where top component is
0487: * opened, false otherwise. If close operation is set to
0488: * CLOSE_EACH, then this param is always true
0489: * @return true if top component is ready to close, false otherwise.
0490: * @deprecated Do not use anymore. Use {@link #canClose()} instead.
0491: * Both parameters are redundant since workspaces are not supported anymore. */
0492: @Deprecated
0493: public boolean canClose(Workspace workspace, boolean last) {
0494: return true;
0495: }
0496:
0497: /** Called only when top component was closed on all workspaces before and
0498: * now is opened for the first time on some workspace. The intent is to
0499: * provide subclasses information about TopComponent's life cycle across
0500: * all existing workspaces.
0501: * Subclasses will usually perform initializing tasks here.
0502: * @deprecated Use {@link #componentOpened} instead. */
0503: @Deprecated
0504: protected void openNotify() {
0505: }
0506:
0507: /** Called only when top component was closed so that now it is closed
0508: * on all workspaces in the system. The intent is to provide subclasses
0509: * information about TopComponent's life cycle across workspaces.
0510: * Subclasses will usually perform cleaning tasks here.
0511: * @deprecated Use {@link #componentClosed} instead.
0512: */
0513: @Deprecated
0514: protected void closeNotify() {
0515: }
0516:
0517: /** Gets the system actions which will appear in the popup menu of this component.
0518: * @return array of system actions for this component
0519: * @deprecated Use {@link #getActions()} instead.
0520: */
0521: @Deprecated
0522: public SystemAction[] getSystemActions() {
0523: return new SystemAction[0];
0524: }
0525:
0526: /** Gets the actions which will appear in the popup menu of this component.
0527: * <p>Subclasses are encouraged to override this method to specify
0528: * their own sets of actions.
0529: * <p>Remember to call the super method when overriding and add your actions
0530: * to the superclass' ones (in some order),
0531: * because the default implementation provides support for standard
0532: * component actions like save, close, and clone.
0533: * @return array of actions for this component
0534: * @since 3.32
0535: */
0536: public javax.swing.Action[] getActions() {
0537: Action[] actions = WindowManager.getDefault()
0538: .topComponentDefaultActions(this );
0539:
0540: SystemAction[] sysActions = getSystemActions();
0541:
0542: // If there are some sys actions (i.e. the subclass overrided the defautl impl) add them.
0543: if (sysActions.length > 0) {
0544: List<Action> acs = new ArrayList<Action>(Arrays
0545: .asList(actions));
0546: acs.addAll(Arrays.asList(sysActions));
0547:
0548: return acs.toArray(new Action[0]);
0549: } else {
0550: return actions;
0551: }
0552: }
0553:
0554: /** Set the close mode for the component.
0555: * @param closeOperation one of {@link #CLOSE_EACH} or {@link #CLOSE_LAST}
0556: * @throws IllegalArgumentException if an unrecognized close mode was supplied
0557: * @see #close()
0558: * @deprecated Do not use. It is redundant since workspaces are not supported anymore. */
0559: @Deprecated
0560: public final void setCloseOperation(final int closeOperation) {
0561: if ((closeOperation != CLOSE_EACH)
0562: && (closeOperation != CLOSE_LAST)) {
0563: throw new IllegalArgumentException(NbBundle.getBundle(
0564: TopComponent.class).getString(
0565: "EXC_UnknownOperation"));
0566: }
0567:
0568: if (this .closeOperation == closeOperation) {
0569: return;
0570: }
0571:
0572: this .closeOperation = closeOperation;
0573: firePropertyChange("closeOperation", null, null); // NOI18N
0574: }
0575:
0576: /** Get the current close mode for this component.
0577: * @return one of {@link #CLOSE_EACH} or {@link #CLOSE_LAST}
0578: * @deprecated Do not use. It is redundant since workspaces are not supported anymore. */
0579: @Deprecated
0580: public final int getCloseOperation() {
0581: return closeOperation;
0582: }
0583:
0584: /**
0585: * Subclasses are encouraged to override this method to provide preferred value
0586: * for unique TopComponent ID returned by {@link org.openide.windows.WindowManager#findTopComponentID}.
0587: *
0588: * Returned value should be a String, preferably describing semantics of
0589: * TopComponent subclass, such as "PieChartViewer" or "HtmlEditor" etc.
0590: * Value is then used by window system as prefix value for creating unique
0591: * TopComponent ID.
0592: *
0593: * Returned String value should be preferably unique, but need not be.
0594: * @since 4.13
0595: */
0596: protected String preferredID() {
0597: Class clazz = getClass();
0598:
0599: if (getPersistenceType() != PERSISTENCE_NEVER
0600: && warnedTCPIClasses.add(clazz)) {
0601: Logger.getAnonymousLogger().warning(
0602: clazz.getName() + " should override preferredID()" //NOI18N
0603: );
0604: }
0605:
0606: String name = getName();
0607:
0608: // fix for #47021 and #47115
0609: if (name == null) {
0610: int ind = clazz.getName().lastIndexOf('.');
0611: name = (ind == -1) ? clazz.getName() : clazz.getName()
0612: .substring(ind + 1);
0613: }
0614:
0615: return name;
0616: }
0617:
0618: /** Called only when top component was closed on all workspaces before and
0619: * now is opened for the first time on some workspace. The intent is to
0620: * provide subclasses information about TopComponent's life cycle across
0621: * all existing workspaces.
0622: * Subclasses will usually perform initializing tasks here.
0623: * @since 2.18 */
0624: protected void componentOpened() {
0625: openNotify();
0626: }
0627:
0628: /** Called only when top component was closed so that now it is closed
0629: * on all workspaces in the system. The intent is to provide subclasses
0630: * information about TopComponent's life cycle across workspaces.
0631: * Subclasses will usually perform cleaning tasks here.
0632: * @since 2.18 */
0633: protected void componentClosed() {
0634: closeNotify();
0635: }
0636:
0637: /** Called when <code>TopComponent</code> is about to be shown.
0638: * Shown here means the component is selected or resides in it own cell
0639: * in container in its <code>Mode</code>. The container is visible and not minimized.
0640: * <p><em>Note:</em> component
0641: * is considered to be shown, even its container window
0642: * is overlapped by another window.</p>
0643: * @since 2.18 */
0644: protected void componentShowing() {
0645: }
0646:
0647: /** Called when <code>TopComponent</code> was hidden. <em>Nore</em>:
0648: * <p><em>Note:</em> Beside typical situations when component is hidden,
0649: * it is considered to be hidden even in that case
0650: * the component is in <code>Mode</code> container hierarchy,
0651: * the cointainer is visible, not minimized,
0652: * but the component is neither selected nor in its own cell,
0653: * i.e. it has it's own tab, but is not the selected one.
0654: * @since 2.18 */
0655: protected void componentHidden() {
0656: }
0657:
0658: /** Called when this component is activated.
0659: * This happens when the parent window of this component gets focus
0660: * (and this component is the preferred one in it), <em>or</em> when
0661: * this component is selected in its window (and its window was already focussed).
0662: * Remember to call the super method.
0663: * The default implementation does nothing.
0664: */
0665: protected void componentActivated() {
0666: }
0667:
0668: /** Called when this component is deactivated.
0669: * This happens when the parent window of this component loses focus
0670: * (and this component is the preferred one in the parent),
0671: * <em>or</em> when this component loses preference in the parent window
0672: * (and the parent window is focussed).
0673: * Remember to call the super method.
0674: * The default implementation does nothing.
0675: */
0676: protected void componentDeactivated() {
0677: }
0678:
0679: /** Request focus for the window holding this top component.
0680: * Also makes the component preferred in that window.
0681: * The component will <em>not</em> be automatically {@link #open opened} first
0682: * if it is not already.
0683: * <p>Subclasses should override this method to transfer focus to desired
0684: * focusable component. <code>TopComponent</code> itself is not focusable.
0685: * See for example {@link org.openide.text.CloneableEditor#requestFocus}.
0686: * @deprecated Use {@link #requestActive} instead to make TopComponent active
0687: * in window system not only focused. This method should have been preserved
0688: * for focus management only but not activation of <code>TopComponent</code> inside
0689: * window system.
0690: */
0691: @Deprecated
0692: @Override
0693: public void requestFocus() {
0694: if (isFocusable()) {
0695: //Issue 44304 - output window is focusable when empty, need some
0696: //way to give it focus
0697: super .requestFocus();
0698: }
0699: }
0700:
0701: /** Request focus for the top component inside focused window.
0702: * Also makes the component preferred in that window.
0703: * The component will <em>not</em> be automatically {@link #open opened} first
0704: * if it is not already.
0705: * <p>Subclasses should override this method to transfer focus to desired
0706: * focusable component. <code>TopComponent</code> itself is not focusable.
0707: * See for example {@link org.openide.text.CloneableEditor#requestFocusInWindow}.
0708: * @deprecated Use {@link #requestActive} instead to make TopComponent active
0709: * in window system not only focused. This method should have been preserved
0710: * for focus management only but not activation of <code>TopComponent</code> inside
0711: * window system.
0712: */
0713: @Deprecated
0714: @Override
0715: public boolean requestFocusInWindow() {
0716: if (isFocusable()) {
0717: return super .requestFocusInWindow();
0718: } else {
0719: return false;
0720: }
0721: }
0722:
0723: /** Activates this <code>TopComponent<code> if it is opened.
0724: * @since 4.13 */
0725: public void requestActive() {
0726: WindowManager.getDefault().topComponentRequestActive(this );
0727: }
0728:
0729: /**
0730: * Attempts to bring the parent <code>Window</code> or <code>Frame</code>
0731: * of this <code>TopComponent<code> to front of other windows.
0732: * @since 5.8
0733: */
0734: public void toFront() {
0735: WindowManager.getDefault().topComponentToFront(this );
0736: }
0737:
0738: /** Selects this <code>TopComponent</code>, if it is opened, but does not activate it
0739: * unless it is in active mode already. */
0740: public void requestVisible() {
0741: WindowManager.getDefault().topComponentRequestVisible(this );
0742: org.netbeans.modules.openide.windows.GlobalActionContextImpl
0743: .blickActionMap(getActionMap());
0744: }
0745:
0746: /**
0747: * Cause this TopComponent's tab to flash or otherwise draw attention to
0748: * itself. This method is thread-safe.
0749: * <p>
0750: * It will remain flashing until either <code>cancelRequestAttention</code>
0751: * is called, the component becomes selected or its activated state changes,
0752: * unless the <code>brief</code> argument is true, in which case it will stop
0753: * after a few second.
0754: * @param brief True if the tab should blink a few times and stop
0755: * @since 5.1
0756: */
0757: public final void requestAttention(final boolean brief) {
0758: //Reentrancy issues - always invoke later
0759: EventQueue.invokeLater(new Runnable() {
0760: public void run() {
0761: if ((attentionGetter != null) && !brief) {
0762: attentionGetter.kill();
0763: } else if (!brief) {
0764: WindowManager.getDefault()
0765: .topComponentRequestAttention(
0766: TopComponent.this );
0767: } else if (attentionGetter != null) {
0768: attentionGetter.reset();
0769: } else {
0770: attentionGetter = new AttentionGetter();
0771: }
0772: }
0773: });
0774: }
0775:
0776: /**
0777: * Cause this TopComponent's tab to stop flashing if it was flashing.
0778: * @since 5.1
0779: */
0780: public final void cancelRequestAttention() {
0781: //Reentrancy issues - always invoke later
0782: EventQueue.invokeLater(new Runnable() {
0783: public void run() {
0784: if (attentionGetter != null) {
0785: attentionGetter.stop();
0786: } else {
0787: WindowManager.getDefault()
0788: .topComponentCancelRequestAttention(
0789: TopComponent.this );
0790: }
0791: }
0792: });
0793: }
0794:
0795: /** Set the name of this top component.
0796: * The default implementation just notifies the window manager.
0797: * @param name the new display name
0798: */
0799: public void setName(final String name) {
0800: String old = getName();
0801:
0802: if ((name != null) && (name.equals(old))) {
0803: return;
0804: }
0805:
0806: super .setName(name);
0807: firePropertyChange("name", old, name); // NOI18N
0808:
0809: // XXX When displayName is null, it is used the name.
0810: WindowManager.getDefault().topComponentDisplayNameChanged(this ,
0811: name);
0812: }
0813:
0814: /** Sets localized display name of this <code>TopComponent</code>.
0815: * @param displayName localized display name which is set
0816: * @since 4.13 */
0817: public void setDisplayName(String displayName) {
0818: String old = this .displayName;
0819:
0820: if ((displayName == old)
0821: || ((displayName != null) && displayName.equals(old))) {
0822: return;
0823: }
0824:
0825: // warning if display name contains html tags
0826: if (BasicHTML.isHTMLString(displayName)) {
0827: Logger
0828: .getAnonymousLogger()
0829: .warning(
0830: "Call of "
0831: + getClass().getName()
0832: + ".setDisplayName(\""
0833: + displayName
0834: + "\")"
0835: + " shouldn't contain any HTML tags. Please use "
0836: + getClass().getName()
0837: + ".setHtmlDisplayName(String)"
0838: + "for such purpose. For details please see http://www.netbeans.org/issues/show_bug.cgi?id=66777.");
0839: }
0840:
0841: this .displayName = displayName;
0842: firePropertyChange("displayName", old, displayName); // NOI18N
0843:
0844: WindowManager.getDefault().topComponentDisplayNameChanged(this ,
0845: displayName);
0846: }
0847:
0848: /** Gets localized display name of this <code>TopComponent</code>.
0849: * @return localized display name or <code>null</code> if there is none
0850: * @since 4.13 */
0851: public String getDisplayName() {
0852: return displayName;
0853: }
0854:
0855: /** Sets localized html display name of this <code>TopComponent</code>.
0856: * Hmtl name usually contains basic html tags for text coloring and style.
0857: * Html name may be null if not needed.
0858: * Must apparently begin with <code><html></code>.
0859: *
0860: * @param htmlDisplayName localized html display name which is set
0861: *
0862: * @since 6.4
0863: */
0864: public void setHtmlDisplayName(String htmlDisplayName) {
0865: String old = this .htmlDisplayName;
0866:
0867: if ((htmlDisplayName == old)
0868: || ((htmlDisplayName != null) && htmlDisplayName
0869: .equals(old))) {
0870: return;
0871: }
0872:
0873: this .htmlDisplayName = htmlDisplayName;
0874: firePropertyChange("htmlDisplayName", old, htmlDisplayName); // NOI18N
0875:
0876: WindowManager.getDefault().topComponentHtmlDisplayNameChanged(
0877: this , htmlDisplayName);
0878: }
0879:
0880: /** Gets localized display name of this <code>TopComponent</code> with added
0881: * html tags for text coloring and/or font style. May return null.
0882: * Must apparently begin with <code><html></code>.
0883: *
0884: * @return localized html display name or <code>null</code> if there is none
0885: *
0886: * @since 6.4
0887: */
0888: public String getHtmlDisplayName() {
0889: return htmlDisplayName;
0890: }
0891:
0892: /** Sets toolTip for this <code>TopComponent</code>, adds notification
0893: * about the change to its <code>WindowManager.TopComponentManager</code>. */
0894: public void setToolTipText(String toolTip) {
0895: if ((toolTip != null) && toolTip.equals(getToolTipText())) {
0896: return;
0897: }
0898:
0899: super .setToolTipText(toolTip);
0900:
0901: // XXX #19428. Container updates name and tooltip in the same handler.
0902: WindowManager.getDefault().topComponentToolTipChanged(this ,
0903: toolTip);
0904: }
0905:
0906: /** Set the icon of this top component.
0907: * The icon will be used for
0908: * the component's representation on the screen, e.g. in a multiwindow's tab.
0909: * The default implementation just notifies the window manager.
0910: * @param icon New components' icon.
0911: */
0912: public void setIcon(final Image icon) {
0913: if (icon == this .icon) {
0914: return;
0915: }
0916:
0917: Image old = this .icon;
0918: this .icon = icon;
0919:
0920: WindowManager.getDefault().topComponentIconChanged(this ,
0921: this .icon); // TEMP
0922:
0923: firePropertyChange("icon", old, icon); // NOI18N
0924: }
0925:
0926: /** @return The icon of the top component */
0927: public Image getIcon() {
0928: return icon;
0929: }
0930:
0931: /** Get the help context for this component.
0932: * Subclasses should generally override this to return specific help.
0933: * @return the help context
0934: */
0935: public HelpCtx getHelpCtx() {
0936: return new HelpCtx(TopComponent.class); // XXX #63303
0937: }
0938:
0939: /** Allows top component to specify list of modes into which can be docked
0940: * by end user. Subclasses should override this method if they want to
0941: * alter docking policy of top component. <p>
0942: * So for example, by returning empty list, top component refuses
0943: * to be docked anywhere. <p>
0944: * Default implementation allows docking anywhere by returning
0945: * input list unchanged.
0946: *
0947: * @param modes list of {@link Mode} which represent all modes of current
0948: * workspace, can contain nulls. Items are structured in logical groups
0949: * separated by null entries. <p>
0950: * Input array also contains special constant modes for docking
0951: * into newly created frames. Their names are "SingleNewMode",
0952: * "MultiNewMode", "SplitNewMode", can be used for their
0953: * recognition. Please note that names and existence of special modes
0954: * can change in future releases.
0955: *
0956: * @return list of {@link Mode} which are available for dock, can contain nulls
0957: * @since 2.14
0958: */
0959: public List<Mode> availableModes(List<Mode> modes) {
0960: return modes;
0961: }
0962:
0963: /** Overrides superclass method, adds possible additional handling of global keystrokes
0964: * in case this <code>TopComoponent</code> is ancestor of focused component. */
0965: protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
0966: int condition, boolean pressed) {
0967: boolean ret = super
0968: .processKeyBinding(ks, e, condition, pressed);
0969:
0970: // XXX #30189 Reason of overriding: to process global shortcut.
0971: if ((JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT == condition)
0972: && (ret == false) && !e.isConsumed()) { // NOI18N
0973:
0974: Keymap km = Lookup.getDefault().lookup(Keymap.class);
0975: Action action = (km != null) ? km.getAction(ks) : null;
0976:
0977: if (action == null) {
0978: return false;
0979: }
0980:
0981: // If necessary create context aware instance.
0982: if (action instanceof ContextAwareAction) {
0983: Action delegate = ((ContextAwareAction) action)
0984: .createContextAwareInstance(getLookup());
0985: if (delegate.isEnabled() || getActivatedNodes() != null)
0986: action = delegate;
0987: //else
0988: // use the global instance which might be enabled if it can survive focus changes
0989:
0990: } else if (SwingUtilities.getWindowAncestor(e
0991: .getComponent()) instanceof java.awt.Dialog) {
0992: // #30303 For 'old type' actions check the transmodal flag,
0993: // if invoked in dialog. See ShorcutAndMenuKeyEventProcessor in core.
0994: Object value = action
0995: .getValue("OpenIDE-Transmodal-Action"); // NOI18N
0996:
0997: if (!Boolean.TRUE.equals(value)) {
0998: return false;
0999: }
1000: }
1001:
1002: if (action.isEnabled()) {
1003: LogRecord rec = new LogRecord(Level.FINER,
1004: "UI_ACTION_KEY_PRESS"); // NOI18N
1005: rec.setParameters(new Object[] { ks, ks.toString(),
1006: action.toString(), action.getClass().getName(),
1007: action.getValue(Action.NAME) });
1008: rec.setResourceBundle(NbBundle
1009: .getBundle(TopComponent.class));
1010: rec.setLoggerName(UILOG.getName());
1011: UILOG.log(rec);
1012:
1013: ActionEvent ev = new ActionEvent(this ,
1014: ActionEvent.ACTION_PERFORMED, Utilities
1015: .keyToString(ks));
1016: action.actionPerformed(ev);
1017: } else {
1018: Toolkit.getDefaultToolkit().beep();
1019: }
1020:
1021: return true;
1022: } else {
1023: return ret;
1024: }
1025: }
1026:
1027: /** Serialize this top component.
1028: * Subclasses wishing to store state must call the super method, then write to the stream.
1029: * @param out the stream to serialize to
1030: */
1031: public void writeExternal(ObjectOutput out) throws IOException {
1032: out.writeObject(new Short(serialVersion));
1033: out.writeInt(closeOperation);
1034: out.writeObject(getName());
1035: out.writeObject(getToolTipText());
1036:
1037: if (getDisplayName() != null) {
1038: out.writeObject(getDisplayName());
1039: }
1040:
1041: Node n = (nodeName == null) ? null : nodeName.getNode();
1042: Node.Handle h = (n == null) ? null : n.getHandle();
1043: out.writeObject(h);
1044: }
1045:
1046: /** Deserialize this top component.
1047: * Subclasses wishing to store state must call the super method, then read from the stream.
1048: * @param in the stream to deserialize from
1049: */
1050: public void readExternal(ObjectInput in) throws IOException,
1051: ClassNotFoundException {
1052: Object firstObject = in.readObject();
1053:
1054: if (firstObject instanceof Integer) {
1055: // backward compatibility read
1056: serialVersion = 0;
1057:
1058: closeOperation = ((Integer) firstObject).intValue();
1059:
1060: // BCR: this is backward compatibility read and is likely not needed
1061: // BCR: anymore. So let's just ignore the read of the data object
1062: // BCR: DataObject obj = (DataObject)in.readObject();
1063: in.readObject();
1064:
1065: super .setName((String) in.readObject());
1066: setToolTipText((String) in.readObject());
1067:
1068: // initialize the connection to a data object
1069:
1070: /* BCR: Remove this as we ignore the DataObject
1071: if (obj != null) {
1072: nodeName = new NodeName (this);
1073: nodeName.attach (obj.getNodeDelegate ());
1074: }
1075: */
1076: } else {
1077: // new serialization
1078: serialVersion = ((Short) firstObject).shortValue();
1079:
1080: closeOperation = in.readInt();
1081: super .setName((String) in.readObject());
1082: setToolTipText((String) in.readObject());
1083:
1084: Object obj = in.readObject();
1085:
1086: if (obj instanceof String) {
1087: setDisplayName((String) obj);
1088: obj = in.readObject();
1089: }
1090:
1091: Node.Handle h = (Node.Handle) obj;
1092:
1093: if (h != null) {
1094: Node n = h.getNode();
1095: NodeName.connect(this , n);
1096: }
1097: }
1098:
1099: if ((closeOperation != CLOSE_EACH)
1100: && (closeOperation != CLOSE_LAST)) {
1101: throw new IOException("invalid closeOperation: "
1102: + closeOperation); // NOI18N
1103: }
1104: }
1105:
1106: /** Delegates instance of replacer class to be serialized instead
1107: * of top component itself. Replacer class calls writeExternal and
1108: * constructor, readExternal and readResolve methods properly, so
1109: 8 any top component can behave like any other externalizable object.
1110: * Subclasses can override this method to perform their
1111: * serialization differentrly */
1112: protected Object writeReplace() throws ObjectStreamException {
1113: return new Replacer(this );
1114: }
1115:
1116: /* Read accessible context
1117: * @return - accessible context
1118: */
1119: public AccessibleContext getAccessibleContext() {
1120: if (accessibleContext == null) {
1121: accessibleContext = new JComponent.AccessibleJComponent() {
1122: public AccessibleRole getAccessibleRole() {
1123: return AccessibleRole.PANEL;
1124: }
1125:
1126: public String getAccessibleName() {
1127: if (accessibleName != null) {
1128: return accessibleName;
1129: }
1130:
1131: return getName();
1132: }
1133:
1134: /* Fix for 19344: Null accessible decription of all TopComponents on JDK1.4 */
1135: public String getToolTipText() {
1136: return TopComponent.this .getToolTipText();
1137: }
1138: };
1139: }
1140:
1141: return accessibleContext;
1142: }
1143:
1144: /** Gets lookup which represents context of this component. By default
1145: * the lookup delegates to result of <code>getActivatedNodes</code>
1146: * method and result of this component <code>ActionMap</code> delegate.
1147: *
1148: * If you override the method in your subclass, the default activatedNodes<->lookup synchronization
1149: * will not be performed. That can influence functionality that relies on activated Nodes being present
1150: * in the TopComponent's lookup. If you want to preserve the synchronization, use <code>associateLookup</code>
1151: * instead.
1152: *
1153: * @return a lookup with designates context of this component
1154: * @see org.openide.util.ContextAwareAction
1155: * @see org.openide.util.Utilities#actionsToPopup(Action[], Lookup)
1156: * @since 3.29
1157: */
1158: public Lookup getLookup() {
1159: return getLookup(true);
1160: }
1161:
1162: /**
1163: * @param init should a lookup be initialized if it is not?
1164: * @return lookup or null
1165: */
1166: private Lookup getLookup(boolean init) {
1167: synchronized (defaultLookupLock) {
1168: if (defaultLookupRef instanceof Lookup) {
1169: return (Lookup) defaultLookupRef;
1170: }
1171:
1172: if (defaultLookupRef instanceof Object[]) {
1173: return (Lookup) ((Object[]) defaultLookupRef)[0];
1174: }
1175:
1176: if (defaultLookupRef instanceof java.lang.ref.Reference) {
1177: Object l = ((java.lang.ref.Reference) defaultLookupRef)
1178: .get();
1179:
1180: if (l instanceof Lookup) {
1181: return (Lookup) l;
1182: }
1183: }
1184:
1185: if (!init) {
1186: return null;
1187: }
1188:
1189: Lookup lookup = new DefaultTopComponentLookup(this ); // Lookup of activated nodes and action map
1190: defaultLookupRef = new java.lang.ref.WeakReference<Lookup>(
1191: lookup);
1192:
1193: return lookup;
1194: }
1195: }
1196:
1197: /** Associates the provided lookup with the component. So it will
1198: * be returned from {@link #getLookup} method.
1199: *
1200: * @param lookup the lookup to associate
1201: * @exception IllegalStateException if there already is a lookup registered
1202: * with this component
1203: * @since 4.23
1204: */
1205: protected final void associateLookup(Lookup lookup) {
1206: setLookup(lookup, true);
1207: }
1208:
1209: /** Associates the provided lookup with the component. So it will
1210: * be returned from {@link #getLookup} method.
1211: *
1212: * @param lookup the lookup to associate
1213: * @param sync synchronize return value of {@link #getActivatedNodes} with the
1214: * content of lookup?
1215: * @exception IllegalStateException if there already is a lookup registered
1216: * with this component
1217: */
1218: final void setLookup(Lookup lookup, boolean sync) {
1219: synchronized (defaultLookupLock) {
1220: if (defaultLookupRef != null) {
1221: throw new IllegalStateException("Trying to set lookup "
1222: + lookup + " but there already is "
1223: + defaultLookupRef + " for component: " + this ); // NOI18N
1224: }
1225:
1226: defaultLookupRef = lookup;
1227:
1228: if (sync) {
1229: defaultLookupRef = new Object[] { defaultLookupRef,
1230: new SynchronizeNodes(lookup) };
1231: }
1232: if (LOG.isLoggable(Level.FINE)) {
1233: LOG.fine("setLookup with " + lookup + " and sync: "
1234: + sync + " on " + this ); // NOI18N
1235: }
1236: }
1237: }
1238:
1239: private void attachNodeName(NodeName nodeName) {
1240: this .nodeName = nodeName;
1241: }
1242:
1243: /** Each top component that wishes to be cloned should implement
1244: * this interface, so CloneAction can check it and call the cloneComponent
1245: * method.
1246: */
1247: public static interface Cloneable {
1248: /** Creates a clone of this component
1249: * @return cloned component.
1250: */
1251: public TopComponent cloneComponent();
1252: }
1253:
1254: /** Registry of all top components.
1255: * There is one instance that can be obtained via {@link TopComponent#getRegistry}
1256: * and it permits listening to the currently selected element, and to
1257: * the activated nodes assigned to it.
1258: */
1259: public static interface Registry {
1260: /** Name of property for the set of opened components. */
1261: public static final String PROP_OPENED = "opened"; // NOI18N
1262:
1263: /** Name of property for the selected top component. */
1264: public static final String PROP_ACTIVATED = "activated"; // NOI18N
1265:
1266: /** Name of property for currently selected nodes. */
1267: public static final String PROP_CURRENT_NODES = "currentNodes"; // NOI18N
1268:
1269: /** Name of property for lastly activated nodes. */
1270: public static final String PROP_ACTIVATED_NODES = "activatedNodes"; // NOI18N
1271:
1272: /** Name of property for listening to TopComponents opened through open() call,
1273: * either by user or programmatically.
1274: * Fired property change event returns opened TopComponent from its getNewValue()
1275: */
1276: public static final String PROP_TC_OPENED = "tcOpened"; // NOI18N
1277:
1278: /** Name of property for listening to TopComponents closed through close() call,
1279: * either by user or programmatically.
1280: * Fired property change event returns closed TopComponent from its getNewValue().
1281: */
1282: public static final String PROP_TC_CLOSED = "tcClosed"; // NOI18N
1283:
1284: /** Get reference to a set of all opened componets in the system.
1285: *
1286: * @return live read-only set of {@link TopComponent}s
1287: */
1288: public Set<TopComponent> getOpened();
1289:
1290: /** Get the currently selected element.
1291: * @return the selected top component, or <CODE>null</CODE> if there is none
1292: */
1293: public TopComponent getActivated();
1294:
1295: /** Getter for the currently selected nodes.
1296: * @return array of nodes or null if no component activated or it returns
1297: * null from getActivatedNodes ().
1298: */
1299: public Node[] getCurrentNodes();
1300:
1301: /** Getter for the lastly activated nodes. Comparing
1302: * to previous method it always remembers the selected nodes
1303: * of the last component that had ones.
1304: *
1305: * @return array of nodes (not null)
1306: */
1307: public Node[] getActivatedNodes();
1308:
1309: /** Add a property change listener.
1310: * @param l the listener to add
1311: */
1312: public void addPropertyChangeListener(PropertyChangeListener l);
1313:
1314: /** Remove a property change listener.
1315: * @param l the listener to remove
1316: */
1317: public void removePropertyChangeListener(
1318: PropertyChangeListener l);
1319: }
1320:
1321: private class AttentionGetter implements ActionListener {
1322: Timer timer = null;
1323:
1324: public AttentionGetter() {
1325: reset();
1326: }
1327:
1328: public void reset() {
1329: assert EventQueue.isDispatchThread();
1330:
1331: if (timer != null) {
1332: timer.stop();
1333: }
1334:
1335: start();
1336: timer = new Timer(3500, this );
1337: timer.setRepeats(false);
1338: timer.start();
1339: }
1340:
1341: private void start() {
1342: WindowManager.getDefault().topComponentRequestAttention(
1343: TopComponent.this );
1344: }
1345:
1346: public void kill() {
1347: timer.stop();
1348: attentionGetter = null;
1349: }
1350:
1351: private void stop() {
1352: if (timer != null) {
1353: timer.stop();
1354: }
1355:
1356: attentionGetter = null;
1357: WindowManager.getDefault()
1358: .topComponentCancelRequestAttention(
1359: TopComponent.this );
1360: attentionGetter = null;
1361: }
1362:
1363: public void actionPerformed(ActionEvent ae) {
1364: stop();
1365: }
1366: }
1367:
1368: /** This class provides the connection between the node name and
1369: * a name of the component.
1370: *
1371: * @deprecated Please do not use. This support class does nothing much
1372: * useful. If you need to synchronize display name of your TopComponent
1373: * with some Node's display name, we recommend you to do it manually in
1374: * your client's code.
1375: */
1376: @Deprecated
1377: public static class NodeName extends NodeAdapter {
1378: /** asociation with top component */
1379: private TopComponent top;
1380:
1381: /** weak ref to node we are attached to or null */
1382: private Reference node;
1383:
1384: /** Listener to node, used for weak listening */
1385: private NodeListener nodeL;
1386:
1387: /** Connects a top component and a node. The name of
1388: * component will be updated as the name of the node
1389: * changes.
1390: *
1391: * @param top top compoonent to modify its name
1392: * @param n node to take name from
1393: *
1394: * @since 4.3
1395: */
1396: public static void connect(TopComponent top, Node n) {
1397: new NodeName(top).attach(n);
1398: }
1399:
1400: /** Constructs new name adapter that
1401: * can be attached to any node and will listen on changes
1402: * of its display name and modify the name of the component.
1403: *
1404: * @param top top component to modify its name
1405: *
1406: * @deprecated Please do not use, public just by an accident.
1407: */
1408: @Deprecated
1409: public NodeName(TopComponent top) {
1410: this .top = top;
1411: }
1412:
1413: /** Listens to Node.PROP_DISPLAY_NAME.
1414: *
1415: * @deprecated Please do not use, public just by an accident.
1416: */
1417: @Deprecated
1418: public void propertyChange(PropertyChangeEvent ev) {
1419: if (ev.getPropertyName().equals(Node.PROP_DISPLAY_NAME)) {
1420: Node n = (Node) node.get();
1421: if (n != null) {
1422: top.setName(n.getDisplayName());
1423: }
1424: }
1425: }
1426:
1427: /** Attaches itself to a given node.
1428: */
1429: private void attach(Node n) {
1430: synchronized (top) {
1431: node = new WeakReference<Node>(n);
1432: nodeL = WeakListeners.create(NodeListener.class, this ,
1433: n);
1434: n.addNodeListener(nodeL);
1435: top.attachNodeName(this );
1436: top.setActivatedNodes(new Node[] { n });
1437: top.setName(n.getDisplayName());
1438: }
1439: }
1440:
1441: private Node getNode() {
1442: return (Node) node.get();
1443: }
1444:
1445: } // end of NodeName
1446:
1447: /** Instance of this class is serialized instead of TopComponent itself.
1448: * Emulates behaviour of serialization of externalizable objects
1449: * to keep TopComponent serialization compatible with previous versions. */
1450: private static final class Replacer implements Serializable {
1451: /** SUID */
1452: static final long serialVersionUID = -8897067133215740572L;
1453:
1454: /** Asociation with top component which is to be serialized using
1455: * this replacer */
1456: transient TopComponent tc;
1457:
1458: public Replacer(TopComponent tc) {
1459: this .tc = tc;
1460: }
1461:
1462: private void writeObject(ObjectOutputStream oos)
1463: throws IOException, ClassNotFoundException {
1464: // write the name of the top component first
1465: oos.writeObject(tc.getClass().getName());
1466:
1467: // and now let top component to serialize itself
1468: tc.writeExternal(oos);
1469: }
1470:
1471: private void readObject(ObjectInputStream ois)
1472: throws IOException, ClassNotFoundException {
1473: // read the name of top component's class, instantiate it
1474: // and read its attributes from the stream
1475: String name = (String) ois.readObject();
1476: name = org.openide.util.Utilities.translate(name);
1477:
1478: try {
1479: ClassLoader loader = Lookup.getDefault().lookup(
1480: ClassLoader.class);
1481:
1482: if (loader == null) {
1483: loader = getClass().getClassLoader();
1484: }
1485:
1486: Class tcClass = Class.forName(name, true, loader);
1487:
1488: // instantiate class event if it has protected or private
1489: // default constructor
1490: java.lang.reflect.Constructor con = tcClass
1491: .getDeclaredConstructor(new Class[0]);
1492: con.setAccessible(true);
1493:
1494: try {
1495: tc = (TopComponent) con.newInstance(new Object[0]);
1496: } finally {
1497: con.setAccessible(false);
1498: }
1499:
1500: tc.readExternal(ois);
1501:
1502: // call readResolve() if present and use resolved value
1503: Method resolveMethod = findReadResolveMethod(tcClass);
1504:
1505: if (resolveMethod != null) {
1506: // check exceptions clause
1507: Class[] result = resolveMethod.getExceptionTypes();
1508:
1509: if ((result.length == 1)
1510: && ObjectStreamException.class
1511: .equals(result[0])) {
1512: // returned value type
1513: if (Object.class.equals(resolveMethod
1514: .getReturnType())) {
1515: // make readResolve accessible (it can have any access modifier)
1516: resolveMethod.setAccessible(true);
1517:
1518: // invoke resolve method and accept its result
1519: try {
1520: TopComponent unresolvedTc = tc;
1521: tc = (TopComponent) resolveMethod
1522: .invoke(tc);
1523:
1524: if (tc == null) {
1525: throw new java.io.InvalidObjectException(
1526: "TopComponent.readResolve() cannot return null." // NOI18N
1527: + " See http://www.netbeans.org/issues/show_bug.cgi?id=27849 for more info." // NOI18N
1528: + " TopComponent:"
1529: + unresolvedTc); // NOI18N
1530: }
1531: } finally {
1532: resolveMethod.setAccessible(false);
1533: }
1534: }
1535: }
1536: }
1537: } catch (Exception exc) {
1538: Throwable th = exc;
1539:
1540: // Extract target exception.
1541: if (th instanceof InvocationTargetException) {
1542: th = ((InvocationTargetException) th)
1543: .getTargetException();
1544: }
1545:
1546: // IOException throw directly.
1547: if (th instanceof IOException) {
1548: throw (IOException) th;
1549: }
1550:
1551: // All others wrap into IOException.
1552: throw (IOException) new IOException(th.toString())
1553: .initCause(th);
1554: }
1555: }
1556:
1557: /** Resolve to original top component instance */
1558: private Object readResolve() throws ObjectStreamException {
1559: return tc;
1560: }
1561:
1562: /** Tries to find readResolve method in given class. Finds
1563: * both public and non-public occurences of the method and
1564: * searches also in superclasses */
1565: private static Method findReadResolveMethod(Class clazz) {
1566: Method result = null;
1567: Class[] params = new Class[0];
1568:
1569: // first try public occurences
1570: try {
1571: result = clazz.getMethod("readResolve", params); // NOI18N
1572: } catch (NoSuchMethodException exc) {
1573: // public readResolve does not exist
1574: // now try non-public occurences; search also in superclasses
1575: for (Class i = clazz; (i != null)
1576: && (i != TopComponent.class); i = i
1577: .getSuperclass()) { // perf: TC and its superclasses do not have readResolve method
1578:
1579: try {
1580: result = i.getDeclaredMethod("readResolve",
1581: params); // NOI18N
1582:
1583: // get out of cycle if method found
1584: break;
1585: } catch (NoSuchMethodException exc2) {
1586: // readResolve does not exist in current class
1587: }
1588: }
1589: }
1590:
1591: return result;
1592: }
1593: }
1594:
1595: // end of Replacer inner class
1596:
1597: /** Synchronization between Lookup and getActivatedNodes
1598: */
1599: private class SynchronizeNodes implements
1600: org.openide.util.LookupListener, Runnable {
1601: private Lookup.Result<Node> res;
1602:
1603: public SynchronizeNodes(Lookup l) {
1604: res = l.lookup(new Lookup.Template<Node>(Node.class));
1605: res.addLookupListener(this );
1606: resultChanged(null);
1607: }
1608:
1609: public void resultChanged(org.openide.util.LookupEvent ev) {
1610: boolean l = LOG.isLoggable(Level.FINE);
1611: if (l) {
1612: LOG.fine("lookup changed for " + TopComponent.this
1613: + " is visible: "
1614: + TopComponent.this .isVisible()); // NOI18N
1615: }
1616: if (TopComponent.this .isVisible()
1617: && EventQueue.isDispatchThread()) {
1618: // run immediatelly
1619: run();
1620: } else {
1621: // replan
1622: EventQueue.invokeLater(this );
1623: }
1624: if (l) {
1625: LOG.fine("lookup changed exit " + TopComponent.this ); // NOI18N
1626: }
1627: }
1628:
1629: public void run() {
1630: boolean l = LOG.isLoggable(Level.FINE);
1631:
1632: Collection<? extends Node> nodes = res.allInstances();
1633:
1634: if (l) {
1635: LOG.fine("setting nodes for " + TopComponent.this
1636: + " to " + nodes); // NOI18N
1637: }
1638: setActivatedNodesImpl(nodes.toArray(new Node[0]));
1639: if (l) {
1640: LOG.fine("setting nodes done for " + TopComponent.this
1641: + " to " + nodes); // NOI18N
1642: }
1643: }
1644: } // end of SynchronizeNodes
1645:
1646: private static class CloneWindowAction extends AbstractAction {
1647: DelegateActionMap am;
1648:
1649: public CloneWindowAction(DelegateActionMap am) {
1650: this .am = am;
1651: }
1652:
1653: public void actionPerformed(ActionEvent evt) {
1654: TopComponent.Cloneable self = (TopComponent.Cloneable) am
1655: .getComponent();
1656: if (self != null) {
1657: TopComponent cloned = self.cloneComponent();
1658: cloned.open();
1659: cloned.requestActive();
1660: }
1661: }
1662: } // end of CloneWindowAction
1663:
1664: private static class CloseWindowAction extends AbstractAction {
1665: DelegateActionMap am;
1666:
1667: public CloseWindowAction(DelegateActionMap am) {
1668: this .am = am;
1669: }
1670:
1671: public void actionPerformed(ActionEvent evt) {
1672: TopComponent self = (TopComponent) am.getComponent();
1673: if (self != null) {
1674: self.close();
1675: }
1676: }
1677: } // end of CloseWindowAction
1678: }
|