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);
+        
+    }
+}