#125657: Showing reasonable actions in the popup of lazyproject's node lazy
authorJaroslav Tulach <jtulach@netbeans.org>
Wed, 13 Feb 2008 12:38:47 +0100
branchlazy
changeset 672737046d0487ce2
parent 67272 47d10df7fc31
child 67274 2373145bddbd
#125657: Showing reasonable actions in the popup of lazyproject's node
projectui/src/org/netbeans/modules/project/ui/Bundle.properties
projectui/src/org/netbeans/modules/project/ui/LazyProject.java
projectui/src/org/netbeans/modules/project/ui/LazyProjectInitializing.java
projectui/test/unit/src/org/netbeans/modules/project/ui/ProjectsRootNodePreferredFromPopupTest.java
     1.1 --- a/projectui/src/org/netbeans/modules/project/ui/Bundle.properties	Wed Feb 13 10:36:21 2008 +0100
     1.2 +++ b/projectui/src/org/netbeans/modules/project/ui/Bundle.properties	Wed Feb 13 12:38:47 2008 +0100
     1.3 @@ -37,6 +37,7 @@
     1.4  # Version 2 license, then the option applies only if the new code is
     1.5  # made subject to such option by the copyright holder.
     1.6  
     1.7 +CTL_LazyProjectInitializing=Initializing...
     1.8  OpenIDE-Module-Name=Project UI
     1.9  OpenIDE-Module-Display-Category=Infrastructure
    1.10  OpenIDE-Module-Short-Description=Supplies the basic user interface for projects in the IDE.
     2.1 --- a/projectui/src/org/netbeans/modules/project/ui/LazyProject.java	Wed Feb 13 10:36:21 2008 +0100
     2.2 +++ b/projectui/src/org/netbeans/modules/project/ui/LazyProject.java	Wed Feb 13 12:38:47 2008 +0100
     2.3 @@ -46,10 +46,13 @@
     2.4  import java.util.Collection;
     2.5  import java.util.Collections;
     2.6  import java.util.Iterator;
     2.7 +import javax.swing.Action;
     2.8  import javax.swing.Icon;
     2.9  import org.netbeans.api.project.Project;
    2.10  import org.netbeans.api.project.ProjectInformation;
    2.11  import org.netbeans.spi.project.ui.LogicalViewProvider;
    2.12 +import org.netbeans.spi.project.ui.support.CommonProjectActions;
    2.13 +import org.openide.actions.CustomizeAction;
    2.14  import org.openide.filesystems.FileObject;
    2.15  import org.openide.filesystems.FileUtil;
    2.16  import org.openide.filesystems.URLMapper;
    2.17 @@ -60,6 +63,7 @@
    2.18  import org.openide.util.Lookup;
    2.19  import org.openide.util.NbBundle;
    2.20  import org.openide.util.Utilities;
    2.21 +import org.openide.util.actions.SystemAction;
    2.22  import org.openide.util.lookup.Lookups;
    2.23  import org.openidex.search.SearchInfo;
    2.24  
    2.25 @@ -132,6 +136,7 @@
    2.26          return null;
    2.27      }
    2.28      
    2.29 +    
    2.30      private final class ProjNode extends AbstractNode {
    2.31          public ProjNode(Lookup lookup) {
    2.32              super(new ProjCh(), lookup);
    2.33 @@ -149,8 +154,28 @@
    2.34          public Image getOpenedIcon(int type) {
    2.35              return getIcon(type);
    2.36          }
    2.37 +
    2.38 +        @Override
    2.39 +        public Action getPreferredAction() {
    2.40 +            OpenProjectList.preferredProject(LazyProject.this);
    2.41 +            return super.getPreferredAction();
    2.42 +        }
    2.43 +
    2.44 +        @Override
    2.45 +        public boolean hasCustomizer() {
    2.46 +            return false;
    2.47 +        }
    2.48 +
    2.49 +        @Override
    2.50 +        public Action[] getActions(boolean context) {
    2.51 +            OpenProjectList.preferredProject(LazyProject.this);
    2.52 +            return new Action[] { 
    2.53 +                SystemAction.get(LazyProjectInitializing.class),
    2.54 +                CommonProjectActions.closeProjectAction(),
    2.55 +                SystemAction.get(CustomizeAction.class),
    2.56 +            };
    2.57 +        }
    2.58      } // end of ProjNode
    2.59 -    
    2.60      private final class ProjCh extends Children.Array {
    2.61          @Override
    2.62          protected Collection<Node> initCollection() {
    2.63 @@ -166,6 +191,5 @@
    2.64              super.addNotify();
    2.65              OpenProjectList.preferredProject(LazyProject.this);
    2.66          }
    2.67 -        
    2.68      }
    2.69  }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/projectui/src/org/netbeans/modules/project/ui/LazyProjectInitializing.java	Wed Feb 13 12:38:47 2008 +0100
     3.3 @@ -0,0 +1,71 @@
     3.4 +/*
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + * 
     3.7 + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
     3.8 + * 
     3.9 + * The contents of this file are subject to the terms of either the GNU
    3.10 + * General Public License Version 2 only ("GPL") or the Common
    3.11 + * Development and Distribution License("CDDL") (collectively, the
    3.12 + * "License"). You may not use this file except in compliance with the
    3.13 + * License. You can obtain a copy of the License at
    3.14 + * http://www.netbeans.org/cddl-gplv2.html
    3.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.16 + * specific language governing permissions and limitations under the
    3.17 + * License.  When distributing the software, include this License Header
    3.18 + * Notice in each file and include the License file at
    3.19 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    3.20 + * particular file as subject to the "Classpath" exception as provided
    3.21 + * by Sun in the GPL Version 2 section of the License file that
    3.22 + * accompanied this code. If applicable, add the following below the
    3.23 + * License Header, with the fields enclosed by brackets [] replaced by
    3.24 + * your own identifying information:
    3.25 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.26 + * 
    3.27 + * If you wish your version of this file to be governed by only the CDDL
    3.28 + * or only the GPL Version 2, indicate your decision by adding
    3.29 + * "[Contributor] elects to include this software in this distribution
    3.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.31 + * single choice of license, a recipient has the option to distribute
    3.32 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.33 + * to extend the choice of license to its licensees as provided above.
    3.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.35 + * Version 2 license, then the option applies only if the new code is
    3.36 + * made subject to such option by the copyright holder.
    3.37 + * 
    3.38 + * Contributor(s):
    3.39 + * 
    3.40 + * Portions Copyrighted 2007 Sun Microsystems, Inc.
    3.41 + */
    3.42 +package org.netbeans.modules.project.ui;
    3.43 +
    3.44 +import org.openide.util.HelpCtx;
    3.45 +import org.openide.util.NbBundle;
    3.46 +import org.openide.util.actions.CallableSystemAction;
    3.47 +
    3.48 +final class LazyProjectInitializing extends CallableSystemAction {
    3.49 +
    3.50 +    public void performAction() {
    3.51 +        assert false;
    3.52 +    }
    3.53 +
    3.54 +    public String getName() {
    3.55 +        return NbBundle.getMessage(LazyProjectInitializing.class, "CTL_LazyProjectInitializing");
    3.56 +    }
    3.57 +
    3.58 +    @Override
    3.59 +    protected void initialize() {
    3.60 +        super.initialize();
    3.61 +        // see org.openide.util.actions.SystemAction.iconResource() Javadoc for more details
    3.62 +        putValue("noIconInMenu", Boolean.TRUE);
    3.63 +        setEnabled(false);
    3.64 +    }
    3.65 +
    3.66 +    public HelpCtx getHelpCtx() {
    3.67 +        return HelpCtx.DEFAULT_HELP;
    3.68 +    }
    3.69 +
    3.70 +    @Override
    3.71 +    protected boolean asynchronous() {
    3.72 +        return false;
    3.73 +    }
    3.74 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/ProjectsRootNodePreferredFromPopupTest.java	Wed Feb 13 12:38:47 2008 +0100
     4.3 @@ -0,0 +1,273 @@
     4.4 +/*
     4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 + * 
     4.7 + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
     4.8 + * 
     4.9 + * The contents of this file are subject to the terms of either the GNU
    4.10 + * General Public License Version 2 only ("GPL") or the Common
    4.11 + * Development and Distribution License("CDDL") (collectively, the
    4.12 + * "License"). You may not use this file except in compliance with the
    4.13 + * License. You can obtain a copy of the License at
    4.14 + * http://www.netbeans.org/cddl-gplv2.html
    4.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    4.16 + * specific language governing permissions and limitations under the
    4.17 + * License.  When distributing the software, include this License Header
    4.18 + * Notice in each file and include the License file at
    4.19 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    4.20 + * particular file as subject to the "Classpath" exception as provided
    4.21 + * by Sun in the GPL Version 2 section of the License file that
    4.22 + * accompanied this code. If applicable, add the following below the
    4.23 + * License Header, with the fields enclosed by brackets [] replaced by
    4.24 + * your own identifying information:
    4.25 + * "Portions Copyrighted [year] [name of copyright owner]"
    4.26 + * 
    4.27 + * If you wish your version of this file to be governed by only the CDDL
    4.28 + * or only the GPL Version 2, indicate your decision by adding
    4.29 + * "[Contributor] elects to include this software in this distribution
    4.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    4.31 + * single choice of license, a recipient has the option to distribute
    4.32 + * your version of this file under either the CDDL, the GPL Version 2 or
    4.33 + * to extend the choice of license to its licensees as provided above.
    4.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    4.35 + * Version 2 license, then the option applies only if the new code is
    4.36 + * made subject to such option by the copyright holder.
    4.37 + * 
    4.38 + * Contributor(s):
    4.39 + * 
    4.40 + * Portions Copyrighted 2007 Sun Microsystems, Inc.
    4.41 + */
    4.42 +
    4.43 +package org.netbeans.modules.project.ui;
    4.44 +
    4.45 +import java.awt.EventQueue;
    4.46 +import java.beans.PropertyChangeEvent;
    4.47 +import java.io.IOException;
    4.48 +import java.net.URL;
    4.49 +import java.util.ArrayList;
    4.50 +import java.util.Arrays;
    4.51 +import java.util.EventObject;
    4.52 +import java.util.List;
    4.53 +import java.util.concurrent.CountDownLatch;
    4.54 +import javax.swing.Action;
    4.55 +import javax.swing.SwingUtilities;
    4.56 +import org.netbeans.api.project.Project;
    4.57 +import org.netbeans.api.project.ProjectManager;
    4.58 +import org.netbeans.junit.MockServices;
    4.59 +import org.netbeans.junit.NbTestCase;
    4.60 +import org.netbeans.modules.project.ui.actions.TestSupport;
    4.61 +import org.netbeans.spi.project.ui.ProjectOpenedHook;
    4.62 +import org.openide.filesystems.FileObject;
    4.63 +import org.openide.filesystems.FileUtil;
    4.64 +import org.openide.filesystems.URLMapper;
    4.65 +import org.openide.nodes.Node;
    4.66 +import org.openide.nodes.NodeEvent;
    4.67 +import org.openide.nodes.NodeListener;
    4.68 +import org.openide.nodes.NodeMemberEvent;
    4.69 +import org.openide.nodes.NodeReorderEvent;
    4.70 +import org.openide.util.ContextAwareAction;
    4.71 +import org.openide.util.lookup.Lookups;
    4.72 +
    4.73 +/** 
    4.74 + *
    4.75 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.76 + */
    4.77 +public class ProjectsRootNodePreferredFromPopupTest extends NbTestCase {
    4.78 +    CountDownLatch first;
    4.79 +    CountDownLatch middle;
    4.80 +    CountDownLatch rest;
    4.81 +    
    4.82 +    public ProjectsRootNodePreferredFromPopupTest(String testName) {
    4.83 +        super(testName);
    4.84 +    }            
    4.85 +
    4.86 +    @Override
    4.87 +    protected void setUp() throws Exception {
    4.88 +        clearWorkDir();
    4.89 +        
    4.90 +        MockServices.setServices(TestSupport.TestProjectFactory.class);
    4.91 +        
    4.92 +        FileObject workDir = FileUtil.toFileObject(getWorkDir());
    4.93 +        assertNotNull(workDir);
    4.94 +        
    4.95 +        first = new CountDownLatch(1);
    4.96 +        middle = new CountDownLatch(1);
    4.97 +        rest = new CountDownLatch(2);
    4.98 +        
    4.99 +        List<URL> list = new ArrayList<URL>();
   4.100 +        List<ExtIcon> icons = new ArrayList<ExtIcon>();
   4.101 +        List<String> names = new ArrayList<String>();
   4.102 +        for (int i = 0; i < 10; i++) {
   4.103 +            FileObject prj = TestSupport.createTestProject(workDir, "prj" + i);
   4.104 +            URL url = URLMapper.findURL(prj, URLMapper.EXTERNAL);
   4.105 +            list.add(url);
   4.106 +            names.add(url.toExternalForm());
   4.107 +            icons.add(new ExtIcon());
   4.108 +            TestSupport.TestProject tmp = (TestSupport.TestProject)ProjectManager.getDefault ().findProject (prj);
   4.109 +            assertNotNull("Project found", tmp);
   4.110 +            CountDownLatch down = i == 0 ? first : (i == 5 ? middle : rest);
   4.111 +            tmp.setLookup(Lookups.singleton(new TestProjectOpenedHookImpl(down)));
   4.112 +        }
   4.113 +        
   4.114 +        OpenProjectListSettings.getInstance().setOpenProjectsURLs(list);
   4.115 +        OpenProjectListSettings.getInstance().setOpenProjectsDisplayNames(names);
   4.116 +        OpenProjectListSettings.getInstance().setOpenProjectsIcons(icons);
   4.117 +    }
   4.118 +
   4.119 +    @Override
   4.120 +    protected void tearDown() throws Exception {
   4.121 +        super.tearDown();
   4.122 +    }
   4.123 +
   4.124 +    public void testPreferencesInOpenCanBeChanged() throws InterruptedException, IOException, Exception {
   4.125 +        Node logicalView = new ProjectsRootNode(ProjectsRootNode.LOGICAL_VIEW);
   4.126 +        L listener = new L();
   4.127 +        logicalView.addNodeListener(listener);
   4.128 +        
   4.129 +        assertEquals("10 children", 10, logicalView.getChildren().getNodesCount());
   4.130 +        listener.assertEvents("None", 0);
   4.131 +        assertEquals("No project opened yet", 0, TestProjectOpenedHookImpl.opened);
   4.132 +        
   4.133 +        for (Node n : logicalView.getChildren().getNodes()) {
   4.134 +            TestSupport.TestProject p = n.getLookup().lookup(TestSupport.TestProject.class);
   4.135 +            assertNull("No project of this type, yet", p);
   4.136 +        }
   4.137 +        
   4.138 +        Node midNode = logicalView.getChildren().getNodes()[5];
   4.139 +        {
   4.140 +            TestSupport.TestProject p = midNode.getLookup().lookup(TestSupport.TestProject.class);
   4.141 +            assertNull("No project of this type, yet", p);
   4.142 +        }
   4.143 +        Project lazyP = midNode.getLookup().lookup(Project.class);
   4.144 +        assertNotNull("Some project is found", lazyP);
   4.145 +        assertEquals("It is lazy project", LazyProject.class, lazyP.getClass());
   4.146 +        
   4.147 +        middle.countDown();
   4.148 +        // not necessary, but to ensure middle really does not run
   4.149 +        Thread.sleep(300);
   4.150 +        assertEquals("Still no processing", 0, TestProjectOpenedHookImpl.opened);
   4.151 +
   4.152 +        
   4.153 +        // make a file of some project selected, that 
   4.154 +        // shall trigger OpenProjectList.preferredProject(lazyP);
   4.155 +        Action[] arr = midNode.getActions(true);
   4.156 +        assertEquals("Three: " + Arrays.asList(arr), 3, arr.length);
   4.157 +        assertAction("Initializ", arr[0], false, midNode);
   4.158 +        assertAction("Close", arr[1], true, midNode);
   4.159 +        assertAction("Custom", arr[2], false, midNode);
   4.160 +        
   4.161 +        first.countDown();
   4.162 +        
   4.163 +        TestProjectOpenedHookImpl.toOpen.await();
   4.164 +
   4.165 +        {
   4.166 +            TestSupport.TestProject p = null;
   4.167 +            for (int i = 0; i < 10; i++) {
   4.168 +                Node midNode2 = logicalView.getChildren().getNodes()[5];
   4.169 +                p = midNode.getLookup().lookup(TestSupport.TestProject.class);
   4.170 +                if (p != null) {
   4.171 +                    break;
   4.172 +                }
   4.173 +                Thread.sleep(100);
   4.174 +            }
   4.175 +            assertNotNull("The right project opened", p);
   4.176 +        }
   4.177 +        
   4.178 +        rest.countDown();
   4.179 +        rest.countDown();
   4.180 +        OpenProjectList.waitProjectsFullyOpen();
   4.181 +        
   4.182 +        assertEquals("All projects opened", 10, TestProjectOpenedHookImpl.opened);
   4.183 +        
   4.184 +
   4.185 +        for (Node n : logicalView.getChildren().getNodes()) {
   4.186 +            TestSupport.TestProject p = n.getLookup().lookup(TestSupport.TestProject.class);
   4.187 +            assertNotNull("Nodes have correct project of this type", p);
   4.188 +        }
   4.189 +    }
   4.190 +    
   4.191 +    private static class L implements NodeListener {
   4.192 +        public List<EventObject> events = new ArrayList<EventObject>();
   4.193 +        
   4.194 +        public void childrenAdded(NodeMemberEvent ev) {
   4.195 +            assertFalse("No event in AWT thread", EventQueue.isDispatchThread());
   4.196 +            events.add(ev);
   4.197 +        }
   4.198 +
   4.199 +        public void childrenRemoved(NodeMemberEvent ev) {
   4.200 +            assertFalse("No event in AWT thread", EventQueue.isDispatchThread());
   4.201 +            events.add(ev);
   4.202 +        }
   4.203 +
   4.204 +        public void childrenReordered(NodeReorderEvent ev) {
   4.205 +            assertFalse("No event in AWT thread", EventQueue.isDispatchThread());
   4.206 +            events.add(ev);
   4.207 +        }
   4.208 +
   4.209 +        public void nodeDestroyed(NodeEvent ev) {
   4.210 +            assertFalse("No event in AWT thread", EventQueue.isDispatchThread());
   4.211 +            events.add(ev);
   4.212 +        }
   4.213 +
   4.214 +        public void propertyChange(PropertyChangeEvent evt) {
   4.215 +            assertFalse("No event in AWT thread", EventQueue.isDispatchThread());
   4.216 +            events.add(evt);
   4.217 +        }
   4.218 +
   4.219 +        final void assertEvents(String string, int i) {
   4.220 +            assertEquals(string + events, i, events.size());
   4.221 +            events.clear();
   4.222 +        }
   4.223 +        
   4.224 +    }
   4.225 +    
   4.226 +    private static class TestProjectOpenedHookImpl extends ProjectOpenedHook {
   4.227 +        
   4.228 +        public static CountDownLatch toOpen = new CountDownLatch(2);
   4.229 +        public static int opened = 0;
   4.230 +        public static int closed = 0;
   4.231 +        
   4.232 +        
   4.233 +        private CountDownLatch toWaitOn;
   4.234 +        
   4.235 +        public TestProjectOpenedHookImpl(CountDownLatch toWaitOn) {
   4.236 +            this.toWaitOn = toWaitOn;
   4.237 +        }
   4.238 +        
   4.239 +        protected void projectClosed() {
   4.240 +            closed++;
   4.241 +        }
   4.242 +        
   4.243 +        protected void projectOpened() {
   4.244 +            if (toWaitOn != null) {
   4.245 +                try {
   4.246 +                    toWaitOn.await();
   4.247 +                } catch (InterruptedException ex) {
   4.248 +                    throw new IllegalStateException(ex);
   4.249 +                }
   4.250 +            }
   4.251 +            opened++;
   4.252 +            toOpen.countDown();
   4.253 +        }
   4.254 +        
   4.255 +    }
   4.256 +
   4.257 +    private void assertAction(String text, Action action, boolean b, Node n) throws Exception {
   4.258 +        final Action clone = action instanceof ContextAwareAction ? 
   4.259 +            ((ContextAwareAction)action).createContextAwareInstance(n.getLookup()) :
   4.260 +            action;
   4.261 +        
   4.262 +        assertTrue("Expecting " + text + " but was " + action, action.getClass().getName().contains(text));
   4.263 +        
   4.264 +        class Is implements Runnable {
   4.265 +            boolean is;
   4.266 +            public void run() {
   4.267 +                is = clone.isEnabled();
   4.268 +            }
   4.269 +        }
   4.270 +        Is enabled = new Is();
   4.271 +        SwingUtilities.invokeAndWait(enabled);
   4.272 +        
   4.273 +        assertEquals("Enabled? " + text + " and: " + b, b, enabled.is);
   4.274 +    }
   4.275 +    
   4.276 +}