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