--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java
+++ a/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java
@@ -45,6 +45,7 @@
package org.netbeans.modules.java.api.common.project;
import java.awt.Dialog;
+import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -384,6 +385,7 @@
@Override
public void invokeAction( final String command, final Lookup context ) throws IllegalArgumentException {
+ assert EventQueue.isDispatchThread();
if (COMMAND_DELETE.equals(command)) {
DefaultProjectOperations.performDefaultDeleteOperation(project);
return ;
--- a/projectui/nbproject/project.xml
+++ a/projectui/nbproject/project.xml
@@ -108,7 +108,7 @@
1
- 1.33
+ 1.43
--- a/projectui/src/org/netbeans/modules/project/ui/actions/ActionsUtil.java
+++ a/projectui/src/org/netbeans/modules/project/ui/actions/ActionsUtil.java
@@ -48,6 +48,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -90,7 +91,7 @@
*/
// #74161: do not cache
// First find out whether there is a project directly in the Lookup
- Set result = new HashSet();
+ Set result = new LinkedHashSet(); // XXX or use OpenProjectList.projectByDisplayName?
for (Project p : lookup.lookupAll(Project.class)) {
result.add(p);
}
--- a/projectui/src/org/netbeans/modules/project/ui/actions/FileAction.java
+++ a/projectui/src/org/netbeans/modules/project/ui/actions/FileAction.java
@@ -113,6 +113,7 @@
r[0] = new Runnable() {
@Override public void run() {
Project[] projects = ActionsUtil.getProjectsFromLookup( context, command );
+ // XXX #64991: handle >1 project (tricky since must pass subset of selection to each)
if ( projects.length != 1 ) {
if (projects.length == 0 && globalProvider(context) != null) {
enable[0] = true;
--- a/projectui/src/org/netbeans/modules/project/ui/actions/MainProjectAction.java
+++ a/projectui/src/org/netbeans/modules/project/ui/actions/MainProjectAction.java
@@ -45,22 +45,20 @@
package org.netbeans.modules.project.ui.actions;
import java.awt.Dialog;
-import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
import java.util.Arrays;
+import java.util.LinkedList;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.project.Project;
-import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.modules.project.ui.NoMainProjectWarning;
import org.netbeans.modules.project.ui.OpenProjectList;
-import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
@@ -72,9 +70,11 @@
import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
-/** Invokes command on the main project.
- *
- * @author Pet Hrebejk
+/**
+ * Similar to {@link ProjectAction} but has a different selection model.
+ * First uses the main project, if set.
+ * Else uses the selected projects, if any.
+ * Finally, if just one project is open, uses that.
*/
public class MainProjectAction extends LookupSensitiveAction implements PropertyChangeListener {
@@ -124,58 +124,27 @@
public @Override void actionPerformed(Lookup context) {
- // first try to find main project
- Project p = OpenProjectList.getDefault().getMainProject();
-
- // then try to find some selected project
- if (p == null) {
- Project[] projects = ActionsUtil.getProjectsFromLookup(context, command);
- if (projects.length == 1) {
- p = projects[0];
- }
- }
-
- // then if there is only one project opened in IDE - use it
- if (p == null) {
- Project[] projects = OpenProjects.getDefault().getOpenProjects();
- if (projects.length == 1) {
- p = projects[0];
- }
- }
+ Project mainProject = OpenProjectList.getDefault().getMainProject();
+ Project[] projects = selection(mainProject, context);
// if no main project or no selected or more than one project opened,
// then show warning and allow choose a main project
- if (p == null) {
+ if (projects.length == 0) {
// show warning, if cancel then return
if (showNoMainProjectWarning (OpenProjectList.getDefault().getOpenProjects (),
- getPresenterName(name, OpenProjectList.getDefault().getMainProject(), p))) {
+ getPresenterName(name, mainProject, projects))) {
return ;
}
- p = OpenProjectList.getDefault().getMainProject();
+ projects = new Project[] {OpenProjectList.getDefault().getMainProject()};
}
- if ( command != null ) {
- ActionProvider ap = p.getLookup().lookup(ActionProvider.class);
- if (ap != null) {
- if (Arrays.asList(ap.getSupportedActions()).contains(command)) {
- ap.invokeAction(command, Lookup.EMPTY);
- } else {
- // #47160: was a supported command (e.g. on a freeform project) but was then removed.
- Toolkit.getDefaultToolkit().beep();
- refreshView(null, false);
- }
- }
- }
- else {
- performer.perform( p );
+ if (command != null && projects.length > 0) {
+ ProjectAction.runSequentially(new LinkedList(Arrays.asList(projects)), this, command);
+ } else if (performer != null && projects.length == 1) {
+ performer.perform(projects[0]);
}
}
-
- // Private methods ---------------------------------------------------------
-
- // Implementation of PropertyChangeListener --------------------------------
-
public @Override void propertyChange( PropertyChangeEvent evt ) {
if (OpenProjectList.PROPERTY_MAIN_PROJECT.equals(evt.getPropertyName()) ||
OpenProjectList.PROPERTY_OPEN_PROJECTS.equals(evt.getPropertyName())) {
@@ -183,50 +152,51 @@
}
}
+ private Project[] selection(Project mainProject, Lookup context) {
+ if (mainProject != null) {
+ return new Project[] {mainProject};
+ }
+ Lookup theContext = context;
+ if (theContext == null) {
+ theContext = LastActivatedWindowLookup.INSTANCE;
+ }
+ if (theContext != null) {
+ Project[] projects = ActionsUtil.getProjectsFromLookup(theContext, command);
+ if (projects.length > 0) {
+ return projects;
+ }
+ }
+ Project[] projects = OpenProjects.getDefault().getOpenProjects();
+ if (projects.length == 1) {
+ return projects;
+ }
+ return new Project[0];
+ }
+
private void refreshView(final Lookup context, boolean immediate) {
Runnable r= new Runnable() {
public @Override void run() {
- Project p = OpenProjectList.getDefault().getMainProject();
- Lookup theContext = context;
+ Project mainProject = OpenProjectList.getDefault().getMainProject();
+ Project[] projects = selection(mainProject, context);
- if (p == null) {
- if (theContext == null) {
- theContext = LastActivatedWindowLookup.INSTANCE;
- }
- if (theContext != null) {
- Project[] projects = ActionsUtil.getProjectsFromLookup(theContext, command);
- if (projects.length == 1) {
- p = projects[0];
- }
- }
- }
-
- if (p == null) {
- Project[] projects = OpenProjects.getDefault().getOpenProjects();
- if (projects.length == 1) {
- p = projects[0];
- }
- }
-
- Project mainProject = OpenProjectList.getDefault().getMainProject();
-
- final String presenterName = getPresenterName(name, mainProject, p);
+ final String presenterName = getPresenterName(name, mainProject, projects);
final boolean enabled;
if ( command == null ) {
- enabled = performer.enable(p);
+ enabled = projects.length == 1 && performer.enable(projects[0]);
}
- else {
- if ( p == null ) {
- enabled = false;
+ else if (projects.length == 0) {
+ enabled = false;
+ } else {
+ boolean e = true;
+ for (Project p : projects) {
+ if (!ActionsUtil.commandSupported(p, command, Lookup.EMPTY)) {
+ e = false;
+ break;
+ }
}
- else if ( ActionsUtil.commandSupported ( p, command, Lookup.EMPTY ) ) {
- enabled = true;
- }
- else {
- enabled = false;
- }
+ enabled = e;
}
Mutex.EVENT.writeAccess(new Runnable() {
@@ -245,22 +215,14 @@
}
}
- private String getPresenterName(String name, Project mPrj, Project cPrj) {
- String toReturn = "";
- Object[] formatterArgs;
- if (mPrj == null) {
- if (cPrj == null) {
- formatterArgs = new Object[] { 0 };
- } else {
- formatterArgs = new Object[] { 1, ProjectUtils.getInformation(cPrj).getDisplayName() };
- }
+ private String getPresenterName(String name, Project mPrj, Project[] cPrj) {
+ if (name == null) {
+ return "";
+ } else if (mPrj == null) {
+ return ActionsUtil.formatProjectSensitiveName(name, cPrj);
} else {
- formatterArgs = new Object[] { -1 };
+ return MessageFormat.format(name, -1);
}
- if (name != null) {
- toReturn = MessageFormat.format(name, formatterArgs);
- }
- return toReturn;
}
private boolean showNoMainProjectWarning(Project[] projects, String action) {
--- a/projectui/src/org/netbeans/modules/project/ui/actions/ProjectAction.java
+++ a/projectui/src/org/netbeans/modules/project/ui/actions/ProjectAction.java
@@ -44,11 +44,17 @@
package org.netbeans.modules.project.ui.actions;
+import java.awt.Toolkit;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.swing.Action;
import javax.swing.Icon;
import org.netbeans.api.project.Project;
+import org.netbeans.spi.project.ActionProgress;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
import org.openide.awt.Actions;
@@ -58,6 +64,7 @@
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
+import org.openide.util.lookup.Lookups;
/** Action sensitive to current project
*
@@ -118,26 +125,56 @@
@Override
protected void actionPerformed( Lookup context ) {
Project[] projects = ActionsUtil.getProjectsFromLookup( context, command );
-
- if ( projects.length == 1 ) {
- if ( command != null ) {
- ActionProvider ap = projects[0].getLookup().lookup(ActionProvider.class);
- LogRecord r = new LogRecord(Level.FINE, "PROJECT_ACTION"); // NOI18N
- r.setResourceBundle(NbBundle.getBundle(ProjectAction.class));
- r.setParameters(new Object[] {
- getClass().getName(),
- projects[0].getClass().getName(),
- getValue(NAME)
- });
- r.setLoggerName(UILOG.getName());
- UILOG.log(r);
- ap.invokeAction( command, Lookup.EMPTY );
+ if (command != null && projects.length > 0) {
+ runSequentially(new LinkedList(Arrays.asList(projects)), this, command);
+ } else if (performer != null && projects.length == 1) {
+ performer.perform(projects[0]);
+ }
+ }
+ static void runSequentially(final Queue queue, final LookupSensitiveAction a, final String command) {
+ Project p = queue.remove();
+ final ActionProvider ap = p.getLookup().lookup(ActionProvider.class);
+ if (ap == null) {
+ return;
+ }
+ if (!Arrays.asList(ap.getSupportedActions()).contains(command)) {
+ // #47160: was a supported command (e.g. on a freeform project) but was then removed.
+ Toolkit.getDefaultToolkit().beep();
+ a.refresh(a.getLookup(), false);
+ return;
+ }
+ LogRecord r = new LogRecord(Level.FINE, "PROJECT_ACTION"); // NOI18N
+ r.setResourceBundle(NbBundle.getBundle(ProjectAction.class));
+ r.setParameters(new Object[] {
+ a.getClass().getName(),
+ p.getClass().getName(),
+ a.getValue(NAME)
+ });
+ r.setLoggerName(UILOG.getName());
+ UILOG.log(r);
+ Mutex.EVENT.writeAccess(new Runnable() {
+ @Override public void run() {
+ if (queue.isEmpty()) {
+ ap.invokeAction(command, Lookup.EMPTY);
+ } else {
+ final AtomicBoolean started = new AtomicBoolean();
+ ap.invokeAction(command, Lookups.singleton(new ActionProgress() {
+ @Override protected void started() {
+ started.set(true);
+ }
+ @Override public void finished(boolean success) {
+ if (success) { // OK, next...
+ runSequentially(queue, a, command);
+ } // else build failed, so skip others
+ }
+ }));
+ if (!started.get()) {
+ // Did not run action for some reason; try others?
+ runSequentially(queue, a, command);
+ }
+ }
}
- else if ( performer != null ) {
- performer.perform( projects[0] );
- }
- }
-
+ });
}
@Override
@@ -147,7 +184,7 @@
Project[] projects = ActionsUtil.getProjectsFromLookup( context, command );
final boolean enable;
if ( command != null ) {
- enable = projects.length == 1;
+ enable = projects.length > 0;
} else if ( performer != null && projects.length == 1 ) {
enable = performer.enable(projects[0]);
} else {
--- a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/ProjectActionTest.java
+++ a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/ProjectActionTest.java
@@ -47,11 +47,11 @@
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
-import javax.swing.KeyStroke;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.junit.MockServices;
import org.netbeans.junit.NbTestCase;
+import org.netbeans.spi.project.ActionProgress;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
import org.openide.filesystems.FileObject;
@@ -77,6 +77,7 @@
private DataObject d2_1;
private DataObject d2_2;
private TestSupport.TestProject project1;
+ private TestActionProvider tap1;
private TestSupport.TestProject project2;
@Override protected boolean runInEQ() {
@@ -98,7 +99,8 @@
d1_2 = DataObject.find(f1_2);
project1 = (TestSupport.TestProject)ProjectManager.getDefault().findProject( p1 );
- project1.setLookup( Lookups.fixed( new Object[] { new TestActionProvider() } ) );
+ tap1 = new TestActionProvider();
+ project1.setLookup(Lookups.singleton(tap1));
p2 = TestSupport.createTestProject( workDir, "project2" );
f2_1 = p2.createData("f2_1.java");
@@ -125,6 +127,24 @@
assertEnablement(action, false);
lookup.change(d1_1, d2_1);
assertEnablement(action, false);
+ TestActionProvider tap2 = new TestActionProvider();
+ project2.setLookup(Lookups.singleton(tap2));
+ lookup.change(d2_1);
+ assertEnablement(action, true);
+ lookup.change(d1_1, d2_1);
+ assertEnablement(action, true);
+ action.actionPerformed(null);
+ assertEquals("[COMMAND]", tap1.invocations.toString());
+ assertEquals("[COMMAND]", tap2.invocations.toString());
+ tap1.listenerSuccess = true;
+ tap2.listenerSuccess = true;
+ action.actionPerformed(null);
+ assertEquals("[COMMAND, COMMAND]", tap1.invocations.toString());
+ assertEquals("[COMMAND, COMMAND]", tap2.invocations.toString());
+ tap1.listenerSuccess = false;
+ action.actionPerformed(null);
+ assertEquals("[COMMAND, COMMAND, COMMAND]", tap1.invocations.toString());
+ assertEquals("[COMMAND, COMMAND]", tap2.invocations.toString());
}
public void testProviderEnablement() throws Exception {
@@ -177,6 +197,7 @@
private String[] ACTIONS = new String[] { COMMAND };
private List invocations = new ArrayList();
+ Boolean listenerSuccess;
public String[] getSupportedActions() {
return ACTIONS;
@@ -186,6 +207,9 @@
if ( COMMAND.equals( command ) ) {
invocations.add( command );
+ if (listenerSuccess != null) {
+ ActionProgress.start(context).finished(listenerSuccess);
+ }
}
else {
throw new IllegalArgumentException();