Index: src/org/openide/explorer/ExplorerUtils.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/ExplorerUtils.java,v retrieving revision 1.2 diff -u -r1.2 ExplorerUtils.java --- src/org/openide/explorer/ExplorerUtils.java 22 Nov 2003 00:11:19 -0000 1.2 +++ src/org/openide/explorer/ExplorerUtils.java 17 Dec 2003 14:41:01 -0000 @@ -36,25 +36,24 @@ * should make your component implement {@link ExplorerManager.Provider} and * {@link org.openide.util.Lookup.Provider} and register actions in your component's {@link ActionMap}:
-public class YourComponent extends TopComponent // or JPanel, etc. - implements ExplorerManager.Provider, Lookup.Provider { +public class YourComponent extends TopComponent +implements ExplorerManager.Provider, Lookup.Provider { private ExplorerManager manager; - private Lookup lookup; public YourComponent() { - manager = new ExplorerManager(); - ActionMap map = getActionMap(); + this (new ExplorerManager(), new ActionMap()); + } + private YourComponent(ExplorerManager em, ActionMap map) { + // following line tells the top component which lookup should be associated with it + super (ExplorerUtils.createLookup (em, map)); + this.manager = em; map.put(DefaultEditorKit.copyAction, ExplorerUtils.actionCopy(manager)); map.put(DefaultEditorKit.cutAction, ExplorerUtils.actionCut(manager)); map.put(DefaultEditorKit.pasteAction, ExplorerUtils.actionPaste(manager)); map.put("delete", ExplorerUtils.actionDelete(manager, true)); // or false - lookup = ExplorerUtils.createLookup (manager, map); - } + } public ExplorerManager getExplorerManager() { return manager; } - public Lookup getLookup() { - return lookup; - } // It is good idea to switch all listeners on and off when the // component is shown or hidden. In the case of TopComponent use: protected void componentActivated() { @@ -70,15 +69,34 @@ * turn the listeners on and off:public class YourComponent extends JPanel - implements ExplorerManager.Provider, Lookup.Provider { - // fields as before... +implements ExplorerManager.Provider, Lookup.Provider { + private ExplorerManager manager; + private Lookup lookup; public YourComponent() { - // ...as before, but add e.g.: + // same as before... + manager = new ExplorerManager(); + ActionMap map = getActionMap(); + map.put(DefaultEditorKit.copyAction, ExplorerUtils.actionCopy(manager)); + map.put(DefaultEditorKit.cutAction, ExplorerUtils.actionCut(manager)); + map.put(DefaultEditorKit.pasteAction, ExplorerUtils.actionPaste(manager)); + map.put("delete", ExplorerUtils.actionDelete(manager, true)); // or false + + // ...but add e.g.: InputMap keys = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); keys.put(KeyStroke.getKeyStroke("control c"), DefaultEditorKit.copyAction); keys.put(KeyStroke.getKeyStroke("control x"), DefaultEditorKit.cutAction); keys.put(KeyStroke.getKeyStroke("control v"), DefaultEditorKit.pasteAction); keys.put(KeyStroke.getKeyStroke("DELETE"), "delete"); + + // ...and initialization of lookup variable + lookup = ExplorerUtils.createLookup (manager, map); + } + // ...method as before and getLookup + public ExplorerManager getExplorerManager() { + return manager; + } + public Lookup getLookup() { + return lookup; } // ...methods as before, but replace componentActivated and // componentDeactivated with e.g.: Index: src/org/openide/windows/TopComponent.java =================================================================== RCS file: /cvs/openide/src/org/openide/windows/TopComponent.java,v retrieving revision 1.108 diff -u -r1.108 TopComponent.java --- src/org/openide/windows/TopComponent.java 22 Nov 2003 00:11:18 -0000 1.108 +++ src/org/openide/windows/TopComponent.java 17 Dec 2003 14:41:01 -0000 @@ -36,6 +36,7 @@ import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; @@ -93,9 +94,9 @@ private static Object defaultLookupLock = new Object (); /** reference to Lookup with default implementation for the - * component + * component or the lookup associated with the component itself */ - private java.lang.ref.Reference defaultLookupRef = new WeakReference(null); + private Object defaultLookupRef; /** Listener to the data object's node or null */ private NodeName nodeName; @@ -120,8 +121,28 @@ /** Create a top component. */ public TopComponent () { + this (null); + } + + /** Creates a top component for a provided lookup that will delegate + * take and synchronize activated nodes and ActionMap from a provided + * lookup. The lookup will also be returned from {@link getLookup} method, + * if not overriden. + * + * @param lookup the lookup to associate with + * @since JST-PENDING + */ + public TopComponent (Lookup lookup) { + if (lookup != null) { + setLookup (lookup, true); + ActionMap map = (ActionMap)lookup.lookup (ActionMap.class); + if (map != null) { + setActionMap (map); + } + } + enableEvents (java.awt.AWTEvent.KEY_EVENT_MASK); - + // #27731 TopComponent itself shouldn't get the focus. // XXX What to do in case nothing in TopComponent is focusable? setFocusable(false); @@ -129,6 +150,7 @@ initActionMap(); } + /** Create a top component associated with a data object. * Currently the data object is used to set the component's name * (which will be updated according to the object's node delegate) by @@ -206,13 +228,11 @@ return; } - // XXX Update lookup. - Object lookup = defaultLookupRef.get (); + Lookup lookup = getLookup (false); if (lookup instanceof DefaultTopComponentLookup) { ((DefaultTopComponentLookup)lookup).updateLookups(activatedNodes); } - Node[] old = this.activatedNodes; this.activatedNodes = activatedNodes; @@ -758,10 +778,29 @@ * @since 3.29 */ public Lookup getLookup() { + return getLookup (true); + } + + + /** + * @param init should a lookup be initialized if it is not? + * @return lookup or null + */ + private Lookup getLookup (boolean init) { synchronized (defaultLookupLock) { - Object l = defaultLookupRef.get (); - if (l instanceof Lookup) { - return (Lookup)l; + if (defaultLookupRef instanceof Lookup) { + return (Lookup)defaultLookupRef; + } + + if (defaultLookupRef instanceof java.lang.ref.Reference) { + Object l = ((java.lang.ref.Reference)defaultLookupRef).get (); + if (l instanceof Lookup) { + return (Lookup)l; + } + } + + if (!init) { + return null; } Lookup lookup = new DefaultTopComponentLookup (this); // Lookup of activated nodes and action map @@ -770,6 +809,27 @@ } } + /** Associates the provided lookup with the component. So it will + * be returned from {@link getLookup} method. + * + * @param lookup the lookup to associate + * @param sync synchronize return value of getActivatedNodes() with the + * content of lookup? + * @exception IllegalStateException if there already is a lookup registered + * with this component + */ + final void setLookup (Lookup lookup, boolean sync) { + synchronized (defaultLookupLock) { + if (defaultLookupRef != null) throw new IllegalStateException ("Trying to set lookup " + lookup + " but there already is " + defaultLookupRef + " for component: " + this); // NOI18N + + defaultLookupRef = lookup; + + if (sync) { + new SynchronizeNodes (lookup); + } + } + } + /** This class provides the connection between the node name and * a name of the component. */ @@ -1031,4 +1091,30 @@ } // end of Replacer inner class + /** Synchronization between Lookup and getActivatedNodes + */ + private class SynchronizeNodes implements org.openide.util.LookupListener, Runnable { + private Lookup.Result res; + + public SynchronizeNodes (Lookup l) { + res = l.lookup (new Lookup.Template (Node.class)); + res.addLookupListener (this); + resultChanged (null); + } + + public void resultChanged (org.openide.util.LookupEvent ev) { + if (TopComponent.this.isVisible () && SwingUtilities.isEventDispatchThread ()) { + // replan + SwingUtilities.invokeLater (this); + } else { + // run immediatelly + run (); + } + } + + public void run () { + setActivatedNodes ((Node[])res.allInstances ().toArray (new Node[0])); + } + + } // end of SynchronizeNodes } Index: test/unit/src/org/openide/explorer/ExplorerUtilCreateLookupTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/explorer/ExplorerUtilCreateLookupTest.java,v retrieving revision 1.1 diff -u -r1.1 ExplorerUtilCreateLookupTest.java --- test/unit/src/org/openide/explorer/ExplorerUtilCreateLookupTest.java 21 Nov 2003 10:52:58 -0000 1.1 +++ test/unit/src/org/openide/explorer/ExplorerUtilCreateLookupTest.java 17 Dec 2003 14:41:02 -0000 @@ -53,21 +53,9 @@ return new NbTestSuite(ExplorerUtilCreateLookupTest.class); } - /** Run all tests in AWT thread */ - public final void run(final junit.framework.TestResult result) { - try { - // XXX ExplorerManager when updating selected nodes - // replanes all firing into AWT thread, therefore the test - // has to run in AWT. - javax.swing.SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - ExplorerUtilCreateLookupTest.super.run (result); - } - }); - } catch (Exception ex) { - ex.printStackTrace(); - throw new IllegalStateException (); - } + + protected boolean runInEQ () { + return true; } /** Setup component with lookup. Index: test/unit/src/org/openide/windows/TopComponentLookupToNodesBridge.java =================================================================== RCS file: test/unit/src/org/openide/windows/TopComponentLookupToNodesBridge.java diff -N test/unit/src/org/openide/windows/TopComponentLookupToNodesBridge.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/windows/TopComponentLookupToNodesBridge.java 17 Dec 2003 14:41:02 -0000 @@ -0,0 +1,116 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.windows; + + +import junit.framework.*; + +import org.netbeans.junit.*; +import org.openide.cookies.*; +import org.openide.nodes.*; +import org.openide.util.*; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; + +/** + * Checks behaviour of a bridge that listens on a provided lookup + * and initializes activated nodes according to the nodes in the + * lookup. + * + * @author Jaroslav Tulach, Jesse Glick + */ +public final class TopComponentLookupToNodesBridge extends NbTestCase { + /** own action map */ + protected javax.swing.ActionMap map; + /** top component we work on */ + protected TopComponent top; + /** instance in the lookup */ + protected InstanceContent ic; + /** its lookup */ + protected Lookup lookup; + + public TopComponentLookupToNodesBridge (String testName) { + super(testName); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { + return new NbTestSuite(TopComponentLookupToNodesBridge.class); + } + + /** Setup component with lookup. + */ + protected void setUp () { + System.setProperty ("org.openide.util.Lookup", "-"); + + map = new javax.swing.ActionMap (); + ic = new InstanceContent (); + ic.add (map); + + lookup = new AbstractLookup (ic); + top = new TopComponent (lookup); + } + + + public void testTheLookupIsReturned () { + assertEquals ("Lookup provided to TC in constructor is returned", lookup, top.getLookup ()); + } + + public void testActionMapIsTakenFromTheLookupIfProvided () { + assertEquals ("Action map is set", map, top.getActionMap ()); + ic.set (java.util.Collections.singleton (new javax.swing.ActionMap ()), null); + assertEquals ("And is not changed (right now) if modified in list", map, top.getActionMap ()); + } + + public void testEmptyLookupGeneratesZeroLengthArray () { + assertNotNull ("Array is there", top.getActivatedNodes ()); + assertEquals ("No nodes", 0, top.getActivatedNodes ().length); + } + + public void testNodeIsThereIfInLookup () { + class Listener implements java.beans.PropertyChangeListener { + public int cnt; + public String name; + + public void propertyChange (java.beans.PropertyChangeEvent ev) { + cnt++; + name = ev.getPropertyName (); + } + } + + Listener l = new Listener (); + top.addPropertyChangeListener (l); + + ic.add (Node.EMPTY); + + assertNotNull ("Array exists", top.getActivatedNodes ()); + assertEquals ("One node", 1, top.getActivatedNodes ().length); + assertEquals ("The node", Node.EMPTY, top.getActivatedNodes ()[0]); + assertEquals ("One PCE", 1, l.cnt); + assertEquals ("Name of property", "activatedNodes", l.name); + + + ic.set (java.util.Collections.nCopies (2, Node.EMPTY), null); + + assertEquals ("Two nodes", 2, top.getActivatedNodes ().length); + assertEquals ("The same", Node.EMPTY, top.getActivatedNodes ()[0]); + assertEquals ("The same", Node.EMPTY, top.getActivatedNodes ()[1]); + assertEquals ("second PCE change", 2, l.cnt); + assertEquals ("Name of property", "activatedNodes", l.name); + + } +}