diff --git a/projectui/src/org/netbeans/modules/project/ui/actions/Actions.java b/projectui/src/org/netbeans/modules/project/ui/actions/Actions.java --- a/projectui/src/org/netbeans/modules/project/ui/actions/Actions.java +++ b/projectui/src/org/netbeans/modules/project/ui/actions/Actions.java @@ -50,11 +50,13 @@ import javax.swing.Icon; import org.netbeans.modules.project.uiapi.ActionsFactory; import org.netbeans.spi.project.ActionProvider; +import org.netbeans.spi.project.ui.support.FileActionPerformer; import org.netbeans.spi.project.ui.support.ProjectActionPerformer; import org.openide.filesystems.FileUtil; import org.openide.util.ContextAwareAction; import org.openide.util.Exceptions; import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.actions.SystemAction; @@ -157,7 +159,12 @@ public Action fileCommandAction(String command, String name, Icon icon) { - return new FileCommandAction( command, name, icon, null ); + return new FileAction( command, name, icon, null ); + } + + @Override + public Action fileSensitiveAction(FileActionPerformer performer, String name, Icon icon) { + return new FileAction(performer, name, icon, null); } // Project specific actions ------------------------------------------------ @@ -292,7 +299,7 @@ // 1-off actions ----------------------------------------------------------- public static Action compileSingle() { - Action a = new FileCommandAction( + Action a = new FileAction( ActionProvider.COMMAND_COMPILE_SINGLE, NbBundle.getMessage(Actions.class, "LBL_CompileSingleAction_Name"), ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/compileSingle.png", true), @@ -303,7 +310,7 @@ } public static Action runSingle() { - Action a = new FileCommandAction( + Action a = new FileAction( ActionProvider.COMMAND_RUN_SINGLE, NbBundle.getMessage(Actions.class, "LBL_RunSingleAction_Name"), ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/runSingle.png", true), @@ -314,7 +321,7 @@ } public static Action testSingle() { - Action a = new FileCommandAction( + Action a = new FileAction( ActionProvider.COMMAND_TEST_SINGLE, NbBundle.getMessage(Actions.class, "LBL_TestSingleAction_Name"), ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/testSingle.png", true), diff --git a/projectui/src/org/netbeans/modules/project/ui/actions/FileCommandAction.java b/projectui/src/org/netbeans/modules/project/ui/actions/FileAction.java rename from projectui/src/org/netbeans/modules/project/ui/actions/FileCommandAction.java rename to projectui/src/org/netbeans/modules/project/ui/actions/FileAction.java --- a/projectui/src/org/netbeans/modules/project/ui/actions/FileCommandAction.java +++ b/projectui/src/org/netbeans/modules/project/ui/actions/FileAction.java @@ -50,92 +50,159 @@ import javax.swing.Icon; import org.netbeans.api.project.Project; import org.netbeans.spi.project.ActionProvider; +import org.netbeans.spi.project.ui.support.FileActionPerformer; import org.openide.awt.Actions; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; +import org.openide.util.ContextAwareAction; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.Mutex; /** An action sensitive to selected node. Used for 1-off actions */ -public final class FileCommandAction extends ProjectAction { - - public FileCommandAction( String command, String namePattern, String iconResource, Lookup lookup ) { +public final class FileAction extends LookupSensitiveAction implements ContextAwareAction { + private String command; + private FileActionPerformer performer; + private final String namePattern; + + public FileAction(String command, String namePattern, Icon icon, Lookup lookup) { + this( command, null, namePattern, icon, lookup ); + } + + public FileAction( String command, String namePattern, String iconResource, Lookup lookup ) { this( command, namePattern, ImageUtilities.loadImageIcon(iconResource, false), lookup ); } - public FileCommandAction( String command, String namePattern, Icon icon, Lookup lookup ) { - super( command, namePattern, icon, lookup ); - assert namePattern != null : "Name patern must not be null"; - String presenterName = ActionsUtil.formatName( getNamePattern(), 0, "" ); + public FileAction( FileActionPerformer performer, String namePattern, Icon icon, Lookup lookup) { + this( null, performer, namePattern, icon, lookup ); + } + + @SuppressWarnings("LeakingThisInConstructor") + private FileAction(String command, FileActionPerformer performer, String namePattern, Icon icon, Lookup lookup) { + super(icon, lookup, new Class[] {Project.class, DataObject.class}); + + assert (command != null || performer != null) && !(command != null && performer != null); // exactly one of the arguments must be provided + + this.command = command; + if ( command != null ) { + ActionsUtil.SHORCUTS_MANAGER.registerAction( command, this ); + } + this.performer = performer; + this.namePattern = namePattern; + + String presenterName = ActionsUtil.formatName( namePattern, 0, "" ); setDisplayName( presenterName ); putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName)); } + public final @Override void putValue( String key, Object value ) { + super.putValue( key, value ); + + if (Action.ACCELERATOR_KEY.equals(key)) { + ActionsUtil.SHORCUTS_MANAGER.registerShortcut( command, value ); + } + } + @Override protected void refresh(final Lookup context, final boolean immediate) { - Runnable r = new Runnable() { - @Override public void run() { - Project[] projects = ActionsUtil.getProjectsFromLookup( context, getCommand() ); - final boolean enable; - final String presenterName; - if ( projects.length != 1 ) { - if (projects.length == 0 && globalProvider(context) != null) { - enable = true; - Collection files = context.lookupAll(DataObject.class); - presenterName = ActionsUtil.formatName(getNamePattern(), files.size(), - files.isEmpty() ? "" : files.iterator().next().getPrimaryFile().getNameExt()); // NOI18N + final Runnable[] r = new Runnable[1]; + final boolean[] enable = new boolean[1]; + final String[] presenterName = new String[1]; + if (command != null) { + r[0] = new Runnable() { + @Override public void run() { + Project[] projects = ActionsUtil.getProjectsFromLookup( context, command ); + if ( projects.length != 1 ) { + if (projects.length == 0 && globalProvider(context) != null) { + enable[0] = true; + Collection files = context.lookupAll(DataObject.class); + presenterName[0] = ActionsUtil.formatName(namePattern, files.size(), + files.isEmpty() ? "" : files.iterator().next().getPrimaryFile().getNameExt()); // NOI18N + } else { + enable[0] = false; // Zero or more than one projects found or command not supported + presenterName[0] = ActionsUtil.formatName(namePattern, 0, ""); + } + } + else { + FileObject[] files = ActionsUtil.getFilesFromLookup( context, projects[0] ); + enable[0] = true; + presenterName[0] = ActionsUtil.formatName( namePattern, files.length, files.length > 0 ? files[0].getNameExt() : "" ); // NOI18N + } + } + }; + } else if (performer != null) { + r[0] = new Runnable() { + @Override public void run() { + Collection dobjs = context.lookupAll(DataObject.class); + if (dobjs.size() == 1) { + FileObject f = dobjs.iterator().next().getPrimaryFile(); + + enable[0] = performer.enable(f); + presenterName[0] = ActionsUtil.formatName(namePattern, 1, f); + } else { + enable[0] = false; + presenterName[0] = ActionsUtil.formatName(namePattern, 0, ""); // NOI18N + } + + } + }; + } + + if (r != null) { + Runnable delegate = new Runnable() { + + @Override + public void run() { + r[0].run(); + Mutex.EVENT.writeAccess(new Runnable() { + @Override public void run() { + putValue("menuText", presenterName[0]); + putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName[0])); + setEnabled(enable[0]); + } + }); + } + }; + if (immediate) { + delegate.run(); } else { - enable = false; // Zero or more than one projects found or command not supported - presenterName = ActionsUtil.formatName(getNamePattern(), 0, ""); + RP.post(delegate); } } - else { - FileObject[] files = ActionsUtil.getFilesFromLookup( context, projects[0] ); - enable = true; - presenterName = ActionsUtil.formatName( getNamePattern(), files.length, files.length > 0 ? files[0].getNameExt() : "" ); // NOI18N - } - Mutex.EVENT.writeAccess(new Runnable() { - @Override public void run() { - putValue("menuText", presenterName); - putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName)); - setEnabled(enable); - } - }); - } - }; - if (immediate) { - r.run(); - } else { - RP.post(r); - } } @Override protected void actionPerformed( Lookup context ) { - Project[] projects = ActionsUtil.getProjectsFromLookup( context, getCommand() ); + if (command != null) { + Project[] projects = ActionsUtil.getProjectsFromLookup( context, command ); - if ( projects.length == 1 ) { - ActionProvider ap = projects[0].getLookup().lookup(ActionProvider.class); - ap.invokeAction( getCommand(), context ); - return; - } - - ActionProvider provider = globalProvider(context); - if (provider != null) { - provider.invokeAction(getCommand(), context); + if ( projects.length == 1 ) { + ActionProvider ap = projects[0].getLookup().lookup(ActionProvider.class); + ap.invokeAction( command, context ); + return; + } + + ActionProvider provider = globalProvider(context); + if (provider != null) { + provider.invokeAction(command, context); + } + } else if (performer != null) { + Collection dobjs = context.lookupAll(DataObject.class); + if (dobjs.size() == 1) { + performer.perform(dobjs.iterator().next().getPrimaryFile()); + } } } @Override public Action createContextAwareInstance( Lookup actionContext ) { - return new FileCommandAction( getCommand(), getNamePattern(), (Icon)getValue( SMALL_ICON ), actionContext ); + return new FileAction( command, performer, namePattern, (Icon)getValue( SMALL_ICON ), actionContext ); } private ActionProvider globalProvider(Lookup context) { for (ActionProvider ap : Lookup.getDefault().lookupAll(ActionProvider.class)) { - if (Arrays.asList(ap.getSupportedActions()).contains(getCommand()) && ap.isActionEnabled(getCommand(), context)) { + if (Arrays.asList(ap.getSupportedActions()).contains(command) && ap.isActionEnabled(command, context)) { return ap; } } diff --git a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileCommandActionTest.java b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileActionTest.java rename from projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileCommandActionTest.java rename to projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileActionTest.java --- a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileCommandActionTest.java +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileActionTest.java @@ -51,14 +51,12 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.swing.Action; import javax.swing.Icon; -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.modules.project.ui.actions.ProjectActionTest.ActionCreator; import org.netbeans.spi.project.ActionProvider; +import org.netbeans.spi.project.ui.support.FileActionPerformer; import org.netbeans.spi.project.ui.support.FileSensitiveActions; -import org.netbeans.spi.project.ui.support.ProjectActionPerformer; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; @@ -67,9 +65,9 @@ import org.openide.util.lookup.Lookups; import org.openide.util.test.MockLookup; -public class FileCommandActionTest extends NbTestCase { +public class FileActionTest extends NbTestCase { - public FileCommandActionTest(String name) { + public FileActionTest(String name) { super( name ); } @@ -118,7 +116,7 @@ public void testCommandEnablement() throws Exception { TestSupport.ChangeableLookup lookup = new TestSupport.ChangeableLookup(); - FileCommandAction action = new FileCommandAction( "COMMAND", "TestFileCommandAction", (Icon)null, lookup ); + FileAction action = new FileAction( "COMMAND", "TestFileCommandAction", (Icon)null, lookup ); assertFalse( "Action should NOT be enabled", action.isEnabled() ); @@ -133,10 +131,45 @@ } + public void testProviderEnablement() throws Exception { + TestSupport.ChangeableLookup lookup = new TestSupport.ChangeableLookup(); + TestActionPerformer tap = new TestActionPerformer(); + FileAction action = new FileAction( tap, "TestFileAction", null,lookup ); + + assertFalse( "Action should NOT be enabled", action.isEnabled() ); + assertEquals( "enable() should NOT be called: ", 0, tap.enableCount ); + + tap.clear(); + tap.on( true ); + assertFalse( "Action should NOT be enabled", action.isEnabled() ); + assertEquals( "enable() should NOT be called: ", 0, tap.enableCount ); + + tap.clear(); + tap.on( false ); + lookup.change(d1_1); + assertFalse( "Action should NOT be enabled", action.isEnabled() ); + assertEquals( "enable() should be called once: ", 1, tap.enableCount ); + assertEquals( "enable() should be called on right file: ", f1_1.toString(), tap.fObj.toString() ); + + tap.clear(); + tap.on( true ); + lookup.change(d1_2); + assertTrue( "Action should be enabled", action.isEnabled() ); + assertEquals( "enable() should be called once: ", 1, tap.enableCount ); + assertEquals( "enable() should be called on right file: ", f1_2.toString(), tap.fObj.toString() ); + + tap.clear(); + tap.on( false ); + lookup.change(d1_1, d2_1); + assertFalse( "Action should NOT be enabled", action.isEnabled() ); + assertEquals( "enable() should NOT be called: ", 0, tap.enableCount ); + + } + public void testAcceleratorsPropagated() { - ProjectActionTest.doTestAcceleratorsPropagated(new ActionCreator() { - public ProjectAction create(Lookup l) { - return new FileCommandAction("command", "TestProjectAction", (Icon) null, l); + TestSupport.doTestAcceleratorsPropagated(new TestSupport.ActionCreator() { + public LookupSensitiveAction create(Lookup l) { + return new FileAction("command", "TestProjectAction", (Icon) null, l); } }, true); } @@ -213,20 +246,20 @@ } - private static class TestActionPerformer implements ProjectActionPerformer { + private static class TestActionPerformer implements FileActionPerformer { private int enableCount; - private Project project; + private FileObject fObj; private boolean on; - public boolean enable( Project project ) { + public boolean enable( FileObject fo ) { enableCount ++; - this.project = project; + this.fObj = fo; return on; } - public void perform( Project project ) { - this.project = project; + public void perform( FileObject fo ) { + this.fObj = fo; } public void on( boolean on ) { @@ -234,7 +267,7 @@ } public void clear() { - this.project = null; + this.fObj = null; enableCount = 0; } diff --git a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/ProjectActionTest.java b/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 +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/ProjectActionTest.java @@ -163,51 +163,13 @@ } public void testAcceleratorsPropagated() { - doTestAcceleratorsPropagated(new ActionCreator() { - public ProjectAction create(Lookup l) { + TestSupport.doTestAcceleratorsPropagated(new TestSupport.ActionCreator() { + public LookupSensitiveAction create(Lookup l) { return new ProjectAction("command", "TestProjectAction", null, l); } }, true); } - public static void doTestAcceleratorsPropagated(ActionCreator creator, boolean testMenus) { - Lookup l1 = Lookups.fixed(new Object[] {"1"}); - Lookup l2 = Lookups.fixed(new Object[] {"2"}); - - ProjectAction a1 = creator.create(l1); - - KeyStroke k1 = KeyStroke.getKeyStroke("shift pressed A"); - KeyStroke k2 = KeyStroke.getKeyStroke("shift pressed A"); - - assertNotNull(k1); - assertNotNull(k2); - - a1.putValue(Action.ACCELERATOR_KEY, k1); - - ProjectAction a2 = creator.create(l2); - - assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY)); - - a2.putValue(Action.ACCELERATOR_KEY, k2); - - assertEquals(k2, a1.getValue(Action.ACCELERATOR_KEY)); - - if (testMenus) { - assertEquals(k2, a2.getMenuPresenter().getAccelerator()); - } - - a1.putValue(Action.ACCELERATOR_KEY, k1); - assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY)); - - if (testMenus) { - assertEquals(k1, a2.getMenuPresenter().getAccelerator()); - } - } - - public static interface ActionCreator { - public ProjectAction create(Lookup l); - } - private static class TestActionProvider implements ActionProvider { public String COMMAND = "COMMAND"; diff --git a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/SetMainProjectTest.java b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/SetMainProjectTest.java --- a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/SetMainProjectTest.java +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/SetMainProjectTest.java @@ -53,7 +53,7 @@ import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.project.ui.OpenProjectList; -import org.netbeans.modules.project.ui.actions.ProjectActionTest.ActionCreator; +import org.netbeans.modules.project.ui.actions.TestSupport.ActionCreator; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Lookup; @@ -72,8 +72,8 @@ } public void testAcceleratorsPropagated() { - ProjectActionTest.doTestAcceleratorsPropagated(new ActionCreator() { - public ProjectAction create(Lookup l) { + TestSupport.doTestAcceleratorsPropagated(new ActionCreator() { + public LookupSensitiveAction create(Lookup l) { return new SetMainProject(l); } }, false); diff --git a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/TestSupport.java b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/TestSupport.java --- a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/TestSupport.java +++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/TestSupport.java @@ -49,6 +49,8 @@ import java.lang.reflect.InvocationTargetException; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.Action; +import javax.swing.KeyStroke; import org.netbeans.api.project.Project; import org.netbeans.modules.project.ui.ProjectsRootNode; import org.netbeans.spi.project.AuxiliaryConfiguration; @@ -64,11 +66,50 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import static junit.framework.TestCase.*; + /** * Help set up org.netbeans.api.project.*Test. * @author Jesse Glick */ public final class TestSupport { + public static interface ActionCreator { + public LookupSensitiveAction create(Lookup l); + } + + public static void doTestAcceleratorsPropagated(ActionCreator creator, boolean testMenus) { + Lookup l1 = Lookups.fixed(new Object[] {"1"}); + Lookup l2 = Lookups.fixed(new Object[] {"2"}); + + Action a1 = creator.create(l1); + + KeyStroke k1 = KeyStroke.getKeyStroke("shift pressed A"); + KeyStroke k2 = KeyStroke.getKeyStroke("shift pressed A"); + + assertNotNull(k1); + assertNotNull(k2); + + a1.putValue(Action.ACCELERATOR_KEY, k1); + + LookupSensitiveAction a2 = creator.create(l2); + + assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY)); + + a2.putValue(Action.ACCELERATOR_KEY, k2); + + assertEquals(k2, a1.getValue(Action.ACCELERATOR_KEY)); + + if (testMenus) { + assertEquals(k2, a2.getMenuPresenter().getAccelerator()); + } + + a1.putValue(Action.ACCELERATOR_KEY, k1); + assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY)); + + if (testMenus) { + assertEquals(k1, a2.getMenuPresenter().getAccelerator()); + } + } public static FileObject createTestProject( FileObject workDir, String name ) throws IOException { FileObject p = workDir.createFolder( name ); diff --git a/projectuiapi/apichanges.xml b/projectuiapi/apichanges.xml --- a/projectuiapi/apichanges.xml +++ b/projectuiapi/apichanges.xml @@ -107,6 +107,23 @@ + + + Adding ability to create a file sensitive action with custom performer + + + + + +

+ Added FileSensitiveActions.fileSensitiveAction(performer, name, icon) method for creating file sensitive actions with custom performers. + This method is a logical counterpart to ProjectSensitiveActions.projectSensitiveAction(performer, name, icon) +

+
+ + + +
Will open project notification diff --git a/projectuiapi/nbproject/project.properties b/projectuiapi/nbproject/project.properties --- a/projectuiapi/nbproject/project.properties +++ b/projectuiapi/nbproject/project.properties @@ -42,7 +42,7 @@ javac.compilerargs=-Xlint -Xlint:-serial javac.source=1.6 -spec.version.base=1.55.0 +spec.version.base=1.56.0 is.autoload=true javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml diff --git a/projectuiapi/src/org/netbeans/modules/project/uiapi/ActionsFactory.java b/projectuiapi/src/org/netbeans/modules/project/uiapi/ActionsFactory.java --- a/projectuiapi/src/org/netbeans/modules/project/uiapi/ActionsFactory.java +++ b/projectuiapi/src/org/netbeans/modules/project/uiapi/ActionsFactory.java @@ -46,11 +46,12 @@ import javax.swing.Action; import javax.swing.Icon; +import org.netbeans.spi.project.ui.support.FileActionPerformer; import org.netbeans.spi.project.ui.support.ProjectActionPerformer; import org.openide.util.ContextAwareAction; /** - * Factory to be implemented bu the ui implementation + * Factory to be implemented by the ui implementation * @author Petr Hrebejk */ public interface ActionsFactory { @@ -90,6 +91,8 @@ // Actions sensitive to file public Action fileCommandAction( String command, String name, Icon icon ); + + public Action fileSensitiveAction( FileActionPerformer performer, String name, Icon icon); public Action renameProjectAction(); diff --git a/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java b/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java --- a/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java +++ b/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java @@ -68,6 +68,7 @@ import org.netbeans.api.project.SourceGroup; import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.spi.project.ui.support.BuildExecutionSupport.Item; +import org.netbeans.spi.project.ui.support.FileActionPerformer; import org.netbeans.spi.project.ui.support.ProjectActionPerformer; import org.netbeans.spi.project.ui.support.ProjectCustomizer; import org.openide.WizardDescriptor; @@ -156,6 +157,9 @@ @Override public Action fileCommandAction(String command, String name, Icon icon) { return new Dummy("fileCommand:" + command); } + @Override public Action fileSensitiveAction(FileActionPerformer performer, String name, Icon icon) { + return new Dummy("fileCommand"); + } }; } diff --git a/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectActionPerformer.java b/projectuiapi/src/org/netbeans/spi/project/ui/support/FileActionPerformer.java copy from projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectActionPerformer.java copy to projectuiapi/src/org/netbeans/spi/project/ui/support/FileActionPerformer.java --- a/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectActionPerformer.java +++ b/projectuiapi/src/org/netbeans/spi/project/ui/support/FileActionPerformer.java @@ -44,27 +44,29 @@ package org.netbeans.spi.project.ui.support; -import org.netbeans.api.project.Project; +import org.netbeans.api.annotations.common.NonNull; +import org.openide.filesystems.FileObject; /** - * Callback interface for project- and main project-sensitive actions. - * @author Petr Hrebejk + * Callback interface for file-sensitive actions. + * @author Jaroslav Bachorik + * @since 1.56.0 */ -public interface ProjectActionPerformer { +public interface FileActionPerformer { /** * Called when the context of the action changes and the action should * be enabled or disabled within the new context, according to the newly - * selected project. - * @param project the currently selected project, or null if no project is selected + * selected file. + * @param file the currently selected file, or null if no file is selected * @return true to enable the action, false to disable it */ - boolean enable(Project project); + boolean enable(FileObject file); /** * Called when the user invokes the action. - * @param project the project this action was invoked for (XXX can this be null or not?) + * @param file the file this action was invoked for */ - void perform(Project project); + void perform(@NonNull FileObject file); } diff --git a/projectuiapi/src/org/netbeans/spi/project/ui/support/FileSensitiveActions.java b/projectuiapi/src/org/netbeans/spi/project/ui/support/FileSensitiveActions.java --- a/projectuiapi/src/org/netbeans/spi/project/ui/support/FileSensitiveActions.java +++ b/projectuiapi/src/org/netbeans/spi/project/ui/support/FileSensitiveActions.java @@ -81,4 +81,24 @@ return Utilities.getActionsFactory().fileCommandAction( command, namePattern, icon ); } + /** + * Creates an action sensitive to the set of currently selected files. + * When performed the action will call {@link FileActionPerformer#perform} + * on the action performer supplied + * as a parameter. The action will only be enabled when exactly one + * file is selected and {@link FileActionPerformer#enable} + * returns true.
+ * Note that it is not guaranteed that {@link FileActionPerformer#enable} + * will be called unless the file selection changes and someone is + * listening to the action or explicitly asks for some of the action's values. + * @param performer an action performer. + * @param namePattern pattern which should be used for determining the action's + * name (label). It takes two parameters a la {@link java.text.MessageFormat}: {0} - number of selected files; + * {1} - name of the first file. + * @param icon icon of the action (or null) + * @return newly created file-sensitive action + */ + public static Action fileSensitiveAction( FileActionPerformer performer, String namePattern, Icon icon ) { + return Utilities.getActionsFactory().fileSensitiveAction( performer, namePattern, icon ); + } }