This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 51571
Collapse All | Expand All

(-)a/projectui/src/org/netbeans/modules/project/ui/actions/Actions.java (-4 / +11 lines)
Lines 50-60 Link Here
50
import javax.swing.Icon;
50
import javax.swing.Icon;
51
import org.netbeans.modules.project.uiapi.ActionsFactory;
51
import org.netbeans.modules.project.uiapi.ActionsFactory;
52
import org.netbeans.spi.project.ActionProvider;
52
import org.netbeans.spi.project.ActionProvider;
53
import org.netbeans.spi.project.ui.support.FileActionPerformer;
53
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
54
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
54
import org.openide.filesystems.FileUtil;
55
import org.openide.filesystems.FileUtil;
55
import org.openide.util.ContextAwareAction;
56
import org.openide.util.ContextAwareAction;
56
import org.openide.util.Exceptions;
57
import org.openide.util.Exceptions;
57
import org.openide.util.ImageUtilities;
58
import org.openide.util.ImageUtilities;
59
import org.openide.util.Lookup;
58
import org.openide.util.NbBundle;
60
import org.openide.util.NbBundle;
59
import org.openide.util.actions.SystemAction;
61
import org.openide.util.actions.SystemAction;
60
62
Lines 157-163 Link Here
157
159
158
    
160
    
159
    public Action fileCommandAction(String command, String name, Icon icon) {
161
    public Action fileCommandAction(String command, String name, Icon icon) {
160
        return new FileCommandAction( command, name, icon, null );
162
        return new FileAction( command, name, icon, null );
163
    }
164
165
    @Override
166
    public Action fileSensitiveAction(FileActionPerformer performer, String name, Icon icon) {
167
        return new FileAction(performer, name, icon, null);
161
    }
168
    }
162
    
169
    
163
    // Project specific actions ------------------------------------------------
170
    // Project specific actions ------------------------------------------------
Lines 292-298 Link Here
292
    // 1-off actions -----------------------------------------------------------
299
    // 1-off actions -----------------------------------------------------------
293
    
300
    
294
    public static Action compileSingle() {
301
    public static Action compileSingle() {
295
        Action a = new FileCommandAction(
302
        Action a = new FileAction(
296
            ActionProvider.COMMAND_COMPILE_SINGLE,
303
            ActionProvider.COMMAND_COMPILE_SINGLE,
297
            NbBundle.getMessage(Actions.class, "LBL_CompileSingleAction_Name"),
304
            NbBundle.getMessage(Actions.class, "LBL_CompileSingleAction_Name"),
298
            ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/compileSingle.png", true),
305
            ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/compileSingle.png", true),
Lines 303-309 Link Here
303
    }
310
    }
304
    
311
    
305
    public static Action runSingle() {
312
    public static Action runSingle() {
306
        Action a = new FileCommandAction(
313
        Action a = new FileAction(
307
            ActionProvider.COMMAND_RUN_SINGLE,
314
            ActionProvider.COMMAND_RUN_SINGLE,
308
            NbBundle.getMessage(Actions.class, "LBL_RunSingleAction_Name"),
315
            NbBundle.getMessage(Actions.class, "LBL_RunSingleAction_Name"),
309
            ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/runSingle.png", true),
316
            ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/runSingle.png", true),
Lines 314-320 Link Here
314
    }
321
    }
315
    
322
    
316
    public static Action testSingle() {
323
    public static Action testSingle() {
317
        Action a = new FileCommandAction(
324
        Action a = new FileAction(
318
            ActionProvider.COMMAND_TEST_SINGLE,
325
            ActionProvider.COMMAND_TEST_SINGLE,
319
            NbBundle.getMessage(Actions.class, "LBL_TestSingleAction_Name"),
326
            NbBundle.getMessage(Actions.class, "LBL_TestSingleAction_Name"),
320
            ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/testSingle.png", true),
327
            ImageUtilities.loadImageIcon("org/netbeans/modules/project/ui/resources/testSingle.png", true),
(-)a/projectui/src/org/netbeans/modules/project/ui/actions/FileCommandAction.java (-51 / +118 lines)
Lines 50-141 Link Here
50
import javax.swing.Icon;
50
import javax.swing.Icon;
51
import org.netbeans.api.project.Project;
51
import org.netbeans.api.project.Project;
52
import org.netbeans.spi.project.ActionProvider;
52
import org.netbeans.spi.project.ActionProvider;
53
import org.netbeans.spi.project.ui.support.FileActionPerformer;
53
import org.openide.awt.Actions;
54
import org.openide.awt.Actions;
54
import org.openide.filesystems.FileObject;
55
import org.openide.filesystems.FileObject;
55
import org.openide.loaders.DataObject;
56
import org.openide.loaders.DataObject;
57
import org.openide.util.ContextAwareAction;
56
import org.openide.util.ImageUtilities;
58
import org.openide.util.ImageUtilities;
57
import org.openide.util.Lookup;
59
import org.openide.util.Lookup;
58
import org.openide.util.Mutex;
60
import org.openide.util.Mutex;
59
61
60
/** An action sensitive to selected node. Used for 1-off actions
62
/** An action sensitive to selected node. Used for 1-off actions
61
 */
63
 */
62
public final class FileCommandAction extends ProjectAction {
64
public final class FileAction extends LookupSensitiveAction implements ContextAwareAction {
63
65
    private String command;
64
    public FileCommandAction( String command, String namePattern, String iconResource, Lookup lookup ) {
66
    private FileActionPerformer performer;
67
    private final String namePattern;
68
    
69
    public FileAction(String command, String namePattern, Icon icon, Lookup lookup) {
70
        this( command, null, namePattern, icon, lookup );
71
    }
72
    
73
    public FileAction( String command, String namePattern, String iconResource, Lookup lookup ) {
65
        this( command, namePattern, ImageUtilities.loadImageIcon(iconResource, false), lookup );
74
        this( command, namePattern, ImageUtilities.loadImageIcon(iconResource, false), lookup );
66
    }
75
    }
67
    
76
    
68
    public FileCommandAction( String command, String namePattern, Icon icon, Lookup lookup ) {
77
    public FileAction( FileActionPerformer performer, String namePattern, Icon icon, Lookup lookup) {
69
        super( command, namePattern, icon, lookup );
78
        this( null, performer, namePattern, icon, lookup );
70
        assert namePattern != null : "Name patern must not be null";
79
    }
71
        String presenterName = ActionsUtil.formatName( getNamePattern(), 0, "" );
80
    
81
    @SuppressWarnings("LeakingThisInConstructor")
82
    private FileAction(String command, FileActionPerformer performer, String namePattern, Icon icon, Lookup lookup) {
83
        super(icon, lookup, new Class<?>[] {Project.class, DataObject.class});
84
        
85
        assert (command != null || performer != null) && !(command != null && performer != null); // exactly one of the arguments must be provided
86
        
87
        this.command = command;
88
        if ( command != null ) {
89
            ActionsUtil.SHORCUTS_MANAGER.registerAction( command, this );
90
        }
91
        this.performer = performer;
92
        this.namePattern = namePattern;
93
94
        String presenterName = ActionsUtil.formatName( namePattern, 0, "" );
72
        setDisplayName( presenterName );
95
        setDisplayName( presenterName );
73
        putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName));
96
        putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName));
74
    }
97
    }
75
    
98
    
99
    public final @Override void putValue( String key, Object value ) {
100
        super.putValue( key, value );
101
        
102
        if (Action.ACCELERATOR_KEY.equals(key)) {
103
            ActionsUtil.SHORCUTS_MANAGER.registerShortcut( command, value );
104
        }   
105
    }
106
    
76
    @Override
107
    @Override
77
    protected void refresh(final Lookup context, final boolean immediate) {
108
    protected void refresh(final Lookup context, final boolean immediate) {
78
        Runnable r = new Runnable() {
109
        final Runnable[] r = new Runnable[1];
79
            @Override public void run() {
110
        final boolean[] enable = new boolean[1];
80
        Project[] projects = ActionsUtil.getProjectsFromLookup( context, getCommand() );
111
        final String[] presenterName = new String[1];
81
        final boolean enable;
112
        if (command != null) {
82
        final String presenterName;
113
            r[0] = new Runnable() {
83
        if ( projects.length != 1 ) {
114
                @Override public void run() {
84
            if (projects.length == 0 && globalProvider(context) != null) {
115
                    Project[] projects = ActionsUtil.getProjectsFromLookup( context, command );
85
                enable = true;
116
                    if ( projects.length != 1 ) {
86
                Collection<? extends DataObject> files = context.lookupAll(DataObject.class);
117
                        if (projects.length == 0 && globalProvider(context) != null) {
87
                presenterName = ActionsUtil.formatName(getNamePattern(), files.size(),
118
                            enable[0] = true;
88
                        files.isEmpty() ? "" : files.iterator().next().getPrimaryFile().getNameExt()); // NOI18N
119
                            Collection<? extends DataObject> files = context.lookupAll(DataObject.class);
120
                            presenterName[0] = ActionsUtil.formatName(namePattern, files.size(),
121
                                    files.isEmpty() ? "" : files.iterator().next().getPrimaryFile().getNameExt()); // NOI18N
122
                        } else {
123
                            enable[0] = false; // Zero or more than one projects found or command not supported
124
                            presenterName[0] = ActionsUtil.formatName(namePattern, 0, "");
125
                        }
126
                    }
127
                    else {
128
                        FileObject[] files = ActionsUtil.getFilesFromLookup( context, projects[0] );
129
                        enable[0] = true;
130
                        presenterName[0] = ActionsUtil.formatName( namePattern, files.length, files.length > 0 ? files[0].getNameExt() : "" ); // NOI18N
131
                    }
132
                }
133
            };
134
        } else if (performer != null) {
135
            r[0] = new Runnable() {
136
                @Override public void run() {
137
                    Collection<? extends DataObject> dobjs = context.lookupAll(DataObject.class);
138
                    if (dobjs.size() == 1) {
139
                        FileObject f = dobjs.iterator().next().getPrimaryFile();
140
141
                        enable[0] = performer.enable(f);
142
                        presenterName[0] = ActionsUtil.formatName(namePattern, 1, f);
143
                    } else {
144
                        enable[0] = false;
145
                        presenterName[0] = ActionsUtil.formatName(namePattern, 0, ""); // NOI18N
146
                    }
147
                    
148
                }
149
            };
150
        }
151
152
        if (r != null) {
153
            Runnable delegate = new Runnable() {
154
155
                @Override
156
                public void run() {
157
                    r[0].run();
158
                    Mutex.EVENT.writeAccess(new Runnable() {
159
                        @Override public void run() {
160
                            putValue("menuText", presenterName[0]);
161
                            putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName[0]));
162
                            setEnabled(enable[0]);
163
                        }
164
                    });
165
                }
166
            };
167
            if (immediate) {
168
                delegate.run();
89
            } else {
169
            } else {
90
                enable = false; // Zero or more than one projects found or command not supported
170
                RP.post(delegate);
91
                presenterName = ActionsUtil.formatName(getNamePattern(), 0, "");
92
            }
171
            }
93
        }
172
        }
94
        else {
95
            FileObject[] files = ActionsUtil.getFilesFromLookup( context, projects[0] );
96
            enable = true;
97
            presenterName = ActionsUtil.formatName( getNamePattern(), files.length, files.length > 0 ? files[0].getNameExt() : "" ); // NOI18N
98
        }
99
        Mutex.EVENT.writeAccess(new Runnable() {
100
            @Override public void run() {
101
        putValue("menuText", presenterName);
102
        putValue(SHORT_DESCRIPTION, Actions.cutAmpersand(presenterName));
103
        setEnabled(enable);
104
            }
105
        });
106
            }
107
        };
108
        if (immediate) {
109
            r.run();
110
        } else {
111
            RP.post(r);
112
        }
113
    }
173
    }
114
    
174
    
115
    @Override
175
    @Override
116
    protected void actionPerformed( Lookup context ) {
176
    protected void actionPerformed( Lookup context ) {
117
        Project[] projects = ActionsUtil.getProjectsFromLookup( context, getCommand() );
177
        if (command != null) {
178
            Project[] projects = ActionsUtil.getProjectsFromLookup( context, command );
118
179
119
        if ( projects.length == 1 ) {            
180
            if ( projects.length == 1 ) {            
120
            ActionProvider ap = projects[0].getLookup().lookup(ActionProvider.class);
181
                ActionProvider ap = projects[0].getLookup().lookup(ActionProvider.class);
121
            ap.invokeAction( getCommand(), context );
182
                ap.invokeAction( command, context );
122
            return;
183
                return;
123
        }
184
            }
124
        
185
125
        ActionProvider provider = globalProvider(context);
186
            ActionProvider provider = globalProvider(context);
126
        if (provider != null) {
187
            if (provider != null) {
127
            provider.invokeAction(getCommand(), context);
188
                provider.invokeAction(command, context);
189
            }
190
        } else if (performer != null) {
191
            Collection<? extends DataObject> dobjs = context.lookupAll(DataObject.class);
192
            if (dobjs.size() == 1) {
193
                performer.perform(dobjs.iterator().next().getPrimaryFile());
194
            }
128
        }
195
        }
129
    }
196
    }
130
197
131
    @Override
198
    @Override
132
    public Action createContextAwareInstance( Lookup actionContext ) {
199
    public Action createContextAwareInstance( Lookup actionContext ) {
133
        return new FileCommandAction( getCommand(), getNamePattern(), (Icon)getValue( SMALL_ICON ), actionContext );
200
        return new FileAction( command, performer, namePattern, (Icon)getValue( SMALL_ICON ), actionContext );
134
    }
201
    }
135
202
136
    private ActionProvider globalProvider(Lookup context) {
203
    private ActionProvider globalProvider(Lookup context) {
137
        for (ActionProvider ap : Lookup.getDefault().lookupAll(ActionProvider.class)) {
204
        for (ActionProvider ap : Lookup.getDefault().lookupAll(ActionProvider.class)) {
138
            if (Arrays.asList(ap.getSupportedActions()).contains(getCommand()) && ap.isActionEnabled(getCommand(), context)) {
205
            if (Arrays.asList(ap.getSupportedActions()).contains(command) && ap.isActionEnabled(command, context)) {
139
                return ap;
206
                return ap;
140
            }
207
            }
141
        }
208
        }
(-)a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/FileCommandActionTest.java (-16 / +49 lines)
Lines 51-64 Link Here
51
import java.util.concurrent.atomic.AtomicInteger;
51
import java.util.concurrent.atomic.AtomicInteger;
52
import javax.swing.Action;
52
import javax.swing.Action;
53
import javax.swing.Icon;
53
import javax.swing.Icon;
54
import org.netbeans.api.project.Project;
55
import org.netbeans.api.project.ProjectManager;
54
import org.netbeans.api.project.ProjectManager;
56
import org.netbeans.junit.MockServices;
55
import org.netbeans.junit.MockServices;
57
import org.netbeans.junit.NbTestCase;
56
import org.netbeans.junit.NbTestCase;
58
import org.netbeans.modules.project.ui.actions.ProjectActionTest.ActionCreator;
59
import org.netbeans.spi.project.ActionProvider;
57
import org.netbeans.spi.project.ActionProvider;
58
import org.netbeans.spi.project.ui.support.FileActionPerformer;
60
import org.netbeans.spi.project.ui.support.FileSensitiveActions;
59
import org.netbeans.spi.project.ui.support.FileSensitiveActions;
61
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
62
import org.openide.filesystems.FileObject;
60
import org.openide.filesystems.FileObject;
63
import org.openide.filesystems.FileUtil;
61
import org.openide.filesystems.FileUtil;
64
import org.openide.loaders.DataObject;
62
import org.openide.loaders.DataObject;
Lines 67-75 Link Here
67
import org.openide.util.lookup.Lookups;
65
import org.openide.util.lookup.Lookups;
68
import org.openide.util.test.MockLookup;
66
import org.openide.util.test.MockLookup;
69
67
70
public class FileCommandActionTest extends NbTestCase {
68
public class FileActionTest extends NbTestCase {
71
    
69
    
72
    public FileCommandActionTest(String name) {
70
    public FileActionTest(String name) {
73
        super( name );
71
        super( name );
74
    }
72
    }
75
73
Lines 118-124 Link Here
118
    
116
    
119
    public void testCommandEnablement() throws Exception {
117
    public void testCommandEnablement() throws Exception {
120
        TestSupport.ChangeableLookup lookup = new TestSupport.ChangeableLookup();
118
        TestSupport.ChangeableLookup lookup = new TestSupport.ChangeableLookup();
121
        FileCommandAction action = new FileCommandAction( "COMMAND", "TestFileCommandAction", (Icon)null, lookup );
119
        FileAction action = new FileAction( "COMMAND", "TestFileCommandAction", (Icon)null, lookup );
122
        
120
        
123
        assertFalse( "Action should NOT be enabled", action.isEnabled() );        
121
        assertFalse( "Action should NOT be enabled", action.isEnabled() );        
124
        
122
        
Lines 133-142 Link Here
133
        
131
        
134
    }
132
    }
135
    
133
    
134
    public void testProviderEnablement() throws Exception {
135
        TestSupport.ChangeableLookup lookup = new TestSupport.ChangeableLookup();
136
        TestActionPerformer tap = new TestActionPerformer();
137
        FileAction action = new FileAction( tap, "TestFileAction", null,lookup );
138
     
139
        assertFalse( "Action should NOT be enabled", action.isEnabled() );                
140
        assertEquals( "enable() should NOT be called: ", 0, tap.enableCount );
141
        
142
        tap.clear();
143
        tap.on( true );
144
        assertFalse( "Action should NOT be enabled", action.isEnabled() );
145
        assertEquals( "enable() should NOT be called: ", 0, tap.enableCount );
146
        
147
        tap.clear();
148
        tap.on( false );
149
        lookup.change(d1_1);
150
        assertFalse( "Action should NOT be enabled", action.isEnabled() );
151
        assertEquals( "enable() should be called once: ", 1, tap.enableCount );
152
        assertEquals( "enable() should be called on right file: ", f1_1.toString(), tap.fObj.toString() );
153
        
154
        tap.clear();
155
        tap.on( true );
156
        lookup.change(d1_2);
157
        assertTrue( "Action should be enabled", action.isEnabled() );        
158
        assertEquals( "enable() should be called once: ", 1, tap.enableCount );
159
        assertEquals( "enable() should be called on right file: ", f1_2.toString(), tap.fObj.toString() );
160
                
161
        tap.clear();
162
        tap.on( false );
163
        lookup.change(d1_1, d2_1);
164
        assertFalse( "Action should NOT be enabled", action.isEnabled() );
165
        assertEquals( "enable() should NOT be called: ", 0, tap.enableCount );
166
        
167
    }
168
    
136
    public void testAcceleratorsPropagated() {
169
    public void testAcceleratorsPropagated() {
137
        ProjectActionTest.doTestAcceleratorsPropagated(new ActionCreator() {
170
        TestSupport.doTestAcceleratorsPropagated(new TestSupport.ActionCreator() {
138
            public ProjectAction create(Lookup l) {
171
            public LookupSensitiveAction create(Lookup l) {
139
                return new FileCommandAction("command", "TestProjectAction", (Icon) null, l);
172
                return new FileAction("command", "TestProjectAction", (Icon) null, l);
140
            }
173
            }
141
        }, true);
174
        }, true);
142
    }
175
    }
Lines 213-232 Link Here
213
        
246
        
214
    }
247
    }
215
    
248
    
216
    private static class TestActionPerformer implements ProjectActionPerformer {
249
    private static class TestActionPerformer implements FileActionPerformer {
217
        
250
        
218
        private int enableCount;
251
        private int enableCount;
219
        private Project project;
252
        private FileObject fObj;
220
        private boolean on;
253
        private boolean on;
221
        
254
        
222
        public boolean enable( Project project ) {
255
        public boolean enable( FileObject fo ) {
223
            enableCount ++;
256
            enableCount ++;
224
            this.project = project;
257
            this.fObj = fo;
225
            return on;
258
            return on;
226
        }
259
        }
227
260
228
        public void perform( Project project ) {
261
        public void perform( FileObject fo ) {
229
            this.project = project;
262
            this.fObj = fo;
230
        }
263
        }
231
        
264
        
232
        public void on( boolean on ) {
265
        public void on( boolean on ) {
Lines 234-240 Link Here
234
        }
267
        }
235
        
268
        
236
        public void clear() {
269
        public void clear() {
237
            this.project = null;
270
            this.fObj = null;
238
            enableCount = 0;
271
            enableCount = 0;
239
        }
272
        }
240
        
273
        
(-)a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/ProjectActionTest.java (-40 / +2 lines)
Lines 163-213 Link Here
163
    }
163
    }
164
    
164
    
165
    public void testAcceleratorsPropagated() {
165
    public void testAcceleratorsPropagated() {
166
        doTestAcceleratorsPropagated(new ActionCreator() {
166
        TestSupport.doTestAcceleratorsPropagated(new TestSupport.ActionCreator() {
167
            public ProjectAction create(Lookup l) {
167
            public LookupSensitiveAction create(Lookup l) {
168
                return new ProjectAction("command", "TestProjectAction", null, l);
168
                return new ProjectAction("command", "TestProjectAction", null, l);
169
            }
169
            }
170
        }, true);
170
        }, true);
171
    }
171
    }
172
    
172
    
173
    public static void doTestAcceleratorsPropagated(ActionCreator creator, boolean testMenus) {
174
        Lookup l1 = Lookups.fixed(new Object[] {"1"});
175
        Lookup l2 = Lookups.fixed(new Object[] {"2"});
176
        
177
        ProjectAction a1 = creator.create(l1);
178
        
179
        KeyStroke k1 = KeyStroke.getKeyStroke("shift pressed A");
180
        KeyStroke k2 = KeyStroke.getKeyStroke("shift pressed A");
181
        
182
        assertNotNull(k1);
183
        assertNotNull(k2);
184
        
185
        a1.putValue(Action.ACCELERATOR_KEY, k1);
186
        
187
        ProjectAction a2 = creator.create(l2);
188
        
189
        assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY));
190
        
191
        a2.putValue(Action.ACCELERATOR_KEY, k2);
192
        
193
        assertEquals(k2, a1.getValue(Action.ACCELERATOR_KEY));
194
        
195
        if (testMenus) {
196
            assertEquals(k2, a2.getMenuPresenter().getAccelerator());
197
        }
198
199
        a1.putValue(Action.ACCELERATOR_KEY, k1);
200
        assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY));
201
        
202
        if (testMenus) {
203
            assertEquals(k1, a2.getMenuPresenter().getAccelerator());
204
        }
205
    }
206
    
207
    public static interface ActionCreator {
208
        public ProjectAction create(Lookup l);
209
    }
210
    
211
    private static class TestActionProvider implements ActionProvider {
173
    private static class TestActionProvider implements ActionProvider {
212
        
174
        
213
        public String COMMAND = "COMMAND";
175
        public String COMMAND = "COMMAND";
(-)a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/SetMainProjectTest.java (-3 / +3 lines)
Lines 53-59 Link Here
53
import org.netbeans.junit.MockServices;
53
import org.netbeans.junit.MockServices;
54
import org.netbeans.junit.NbTestCase;
54
import org.netbeans.junit.NbTestCase;
55
import org.netbeans.modules.project.ui.OpenProjectList;
55
import org.netbeans.modules.project.ui.OpenProjectList;
56
import org.netbeans.modules.project.ui.actions.ProjectActionTest.ActionCreator;
56
import org.netbeans.modules.project.ui.actions.TestSupport.ActionCreator;
57
import org.openide.filesystems.FileObject;
57
import org.openide.filesystems.FileObject;
58
import org.openide.filesystems.FileUtil;
58
import org.openide.filesystems.FileUtil;
59
import org.openide.util.Lookup;
59
import org.openide.util.Lookup;
Lines 72-79 Link Here
72
    }
72
    }
73
    
73
    
74
    public void testAcceleratorsPropagated() {
74
    public void testAcceleratorsPropagated() {
75
        ProjectActionTest.doTestAcceleratorsPropagated(new ActionCreator() {
75
        TestSupport.doTestAcceleratorsPropagated(new ActionCreator() {
76
            public ProjectAction create(Lookup l) {
76
            public LookupSensitiveAction create(Lookup l) {
77
                return new SetMainProject(l);
77
                return new SetMainProject(l);
78
            }
78
            }
79
        }, false);
79
        }, false);
(-)a/projectui/test/unit/src/org/netbeans/modules/project/ui/actions/TestSupport.java (+41 lines)
Lines 49-54 Link Here
49
import java.lang.reflect.InvocationTargetException;
49
import java.lang.reflect.InvocationTargetException;
50
import java.util.logging.Level;
50
import java.util.logging.Level;
51
import java.util.logging.Logger;
51
import java.util.logging.Logger;
52
import javax.swing.Action;
53
import javax.swing.KeyStroke;
52
import org.netbeans.api.project.Project;
54
import org.netbeans.api.project.Project;
53
import org.netbeans.modules.project.ui.ProjectsRootNode;
55
import org.netbeans.modules.project.ui.ProjectsRootNode;
54
import org.netbeans.spi.project.AuxiliaryConfiguration;
56
import org.netbeans.spi.project.AuxiliaryConfiguration;
Lines 64-74 Link Here
64
import org.w3c.dom.Node;
66
import org.w3c.dom.Node;
65
import org.w3c.dom.NodeList;
67
import org.w3c.dom.NodeList;
66
68
69
import static junit.framework.TestCase.*;
70
67
/**
71
/**
68
 * Help set up org.netbeans.api.project.*Test.
72
 * Help set up org.netbeans.api.project.*Test.
69
 * @author Jesse Glick
73
 * @author Jesse Glick
70
 */
74
 */
71
public final class TestSupport {
75
public final class TestSupport {
76
    public static interface ActionCreator {
77
        public LookupSensitiveAction create(Lookup l);
78
    }
79
    
80
    public static void doTestAcceleratorsPropagated(ActionCreator creator, boolean testMenus) {
81
        Lookup l1 = Lookups.fixed(new Object[] {"1"});
82
        Lookup l2 = Lookups.fixed(new Object[] {"2"});
83
        
84
        Action a1 = creator.create(l1);
85
        
86
        KeyStroke k1 = KeyStroke.getKeyStroke("shift pressed A");
87
        KeyStroke k2 = KeyStroke.getKeyStroke("shift pressed A");
88
        
89
        assertNotNull(k1);
90
        assertNotNull(k2);
91
        
92
        a1.putValue(Action.ACCELERATOR_KEY, k1);
93
        
94
        LookupSensitiveAction a2 = creator.create(l2);
95
        
96
        assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY));
97
        
98
        a2.putValue(Action.ACCELERATOR_KEY, k2);
99
        
100
        assertEquals(k2, a1.getValue(Action.ACCELERATOR_KEY));
101
        
102
        if (testMenus) {
103
            assertEquals(k2, a2.getMenuPresenter().getAccelerator());
104
        }
105
106
        a1.putValue(Action.ACCELERATOR_KEY, k1);
107
        assertEquals(k1, a2.getValue(Action.ACCELERATOR_KEY));
108
        
109
        if (testMenus) {
110
            assertEquals(k1, a2.getMenuPresenter().getAccelerator());
111
        }
112
    }
72
    
113
    
73
    public static FileObject createTestProject( FileObject workDir, String name ) throws IOException {
114
    public static FileObject createTestProject( FileObject workDir, String name ) throws IOException {
74
        FileObject p = workDir.createFolder( name );
115
        FileObject p = workDir.createFolder( name );
(-)a/projectuiapi/apichanges.xml (+17 lines)
Lines 107-112 Link Here
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
108
109
    <changes>
109
    <changes>
110
        <change id="FileSensitiveAction.performer">
111
            <api name="general"/>
112
            <summary>Adding ability to create a file sensitive action with custom performer</summary>
113
            <version major="1" minor="56"/>
114
            <date day="13" month="1" year="2012"/>
115
            <author login="yardus"/>
116
            <compatibility addition="yes"/>
117
            <description>
118
                <p>
119
                    Added <code>FileSensitiveActions.fileSensitiveAction(performer, name, icon)</code> method for creating file sensitive actions with custom performers.
120
                    This method is a logical counterpart to <code>ProjectSensitiveActions.projectSensitiveAction(performer, name, icon)</code>
121
                </p>
122
            </description>
123
            <class package="org.netbeans.spi.project.ui.support" name="FileSensitiveActions"/>
124
            <class package="org.netbeans.spi.project.ui.support" name="FileActionPerformer"/>
125
            <issue number="51571"/>
126
        </change>
110
        <change id="willOpenProjects">
127
        <change id="willOpenProjects">
111
            <api name="general"/>
128
            <api name="general"/>
112
            <summary>Will open project notification</summary>
129
            <summary>Will open project notification</summary>
(-)a/projectuiapi/nbproject/project.properties (-1 / +1 lines)
Lines 42-48 Link Here
42
42
43
javac.compilerargs=-Xlint -Xlint:-serial
43
javac.compilerargs=-Xlint -Xlint:-serial
44
javac.source=1.6
44
javac.source=1.6
45
spec.version.base=1.55.0
45
spec.version.base=1.56.0
46
is.autoload=true
46
is.autoload=true
47
javadoc.arch=${basedir}/arch.xml
47
javadoc.arch=${basedir}/arch.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/projectuiapi/src/org/netbeans/modules/project/uiapi/ActionsFactory.java (-1 / +4 lines)
Lines 46-56 Link Here
46
46
47
import javax.swing.Action;
47
import javax.swing.Action;
48
import javax.swing.Icon;
48
import javax.swing.Icon;
49
import org.netbeans.spi.project.ui.support.FileActionPerformer;
49
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
50
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
50
import org.openide.util.ContextAwareAction;
51
import org.openide.util.ContextAwareAction;
51
52
52
/**
53
/**
53
 * Factory to be implemented bu the ui implementation
54
 * Factory to be implemented by the ui implementation
54
 * @author Petr Hrebejk
55
 * @author Petr Hrebejk
55
 */
56
 */
56
public interface ActionsFactory {
57
public interface ActionsFactory {
Lines 90-95 Link Here
90
    // Actions sensitive to file
91
    // Actions sensitive to file
91
    
92
    
92
    public Action fileCommandAction( String command, String name, Icon icon );
93
    public Action fileCommandAction( String command, String name, Icon icon );
94
    
95
    public Action fileSensitiveAction( FileActionPerformer performer, String name, Icon icon);
93
96
94
    public Action renameProjectAction();
97
    public Action renameProjectAction();
95
98
(-)a/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java (+4 lines)
Lines 68-73 Link Here
68
import org.netbeans.api.project.SourceGroup;
68
import org.netbeans.api.project.SourceGroup;
69
import org.netbeans.api.project.ui.OpenProjects;
69
import org.netbeans.api.project.ui.OpenProjects;
70
import org.netbeans.spi.project.ui.support.BuildExecutionSupport.Item;
70
import org.netbeans.spi.project.ui.support.BuildExecutionSupport.Item;
71
import org.netbeans.spi.project.ui.support.FileActionPerformer;
71
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
72
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
72
import org.netbeans.spi.project.ui.support.ProjectCustomizer;
73
import org.netbeans.spi.project.ui.support.ProjectCustomizer;
73
import org.openide.WizardDescriptor;
74
import org.openide.WizardDescriptor;
Lines 156-161 Link Here
156
            @Override public Action fileCommandAction(String command, String name, Icon icon) {
157
            @Override public Action fileCommandAction(String command, String name, Icon icon) {
157
                return new Dummy("fileCommand:" + command);
158
                return new Dummy("fileCommand:" + command);
158
            }
159
            }
160
            @Override public Action fileSensitiveAction(FileActionPerformer performer, String name, Icon icon) {
161
                return new Dummy("fileCommand");
162
            }
159
        };
163
        };
160
    }
164
    }
161
165
(-)a/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectActionPerformer.java (-9 / +11 lines)
Lines 44-70 Link Here
44
44
45
package org.netbeans.spi.project.ui.support;
45
package org.netbeans.spi.project.ui.support;
46
46
47
import org.netbeans.api.project.Project;
47
import org.netbeans.api.annotations.common.NonNull;
48
import org.openide.filesystems.FileObject;
48
49
49
/**
50
/**
50
 * Callback interface for project- and main project-sensitive actions.
51
 * Callback interface for file-sensitive actions.
51
 * @author Petr Hrebejk
52
 * @author Jaroslav Bachorik
53
 * @since 1.56.0
52
 */
54
 */
53
public interface ProjectActionPerformer {
55
public interface FileActionPerformer {
54
56
55
    /**
57
    /**
56
     * Called when the context of the action changes and the action should
58
     * Called when the context of the action changes and the action should
57
     * be enabled or disabled within the new context, according to the newly
59
     * be enabled or disabled within the new context, according to the newly
58
     * selected project.
60
     * selected file.
59
     * @param project the currently selected project, or null if no project is selected
61
     * @param file the currently selected file, or null if no file is selected
60
     * @return true to enable the action, false to disable it
62
     * @return true to enable the action, false to disable it
61
     */
63
     */
62
    boolean enable(Project project);
64
    boolean enable(FileObject file);
63
        
65
        
64
    /**
66
    /**
65
     * Called when the user invokes the action.
67
     * Called when the user invokes the action.
66
     * @param project the project this action was invoked for (XXX can this be null or not?)
68
     * @param file the file this action was invoked for
67
     */
69
     */
68
    void perform(Project project);
70
    void perform(@NonNull FileObject file);
69
    
71
    
70
}
72
}
(-)a/projectuiapi/src/org/netbeans/spi/project/ui/support/FileSensitiveActions.java (+20 lines)
Lines 81-84 Link Here
81
        return Utilities.getActionsFactory().fileCommandAction( command, namePattern, icon );
81
        return Utilities.getActionsFactory().fileCommandAction( command, namePattern, icon );
82
    }
82
    }
83
    
83
    
84
    /**
85
     * Creates an action sensitive to the set of currently selected files.
86
     * When performed the action will call {@link FileActionPerformer#perform}
87
     * on the action performer supplied
88
     * as a parameter. The action will only be enabled when exactly one 
89
     * file is selected and {@link FileActionPerformer#enable}
90
     * returns true.<BR>
91
     * Note that it is not guaranteed that {@link FileActionPerformer#enable}
92
     * will be called unless the file selection changes and someone is
93
     * listening to the action or explicitly asks for some of the action's values.
94
     * @param performer an action performer. 
95
     * @param namePattern pattern which should be used for determining the action's
96
     *        name (label). It takes two parameters a la {@link java.text.MessageFormat}: <code>{0}</code> - number of selected files;
97
     *        <code>{1}</code> - name of the first file.
98
     * @param icon icon of the action (or null)
99
     * @return newly created file-sensitive action
100
     */    
101
    public static Action fileSensitiveAction( FileActionPerformer performer, String namePattern, Icon icon ) {
102
        return Utilities.getActionsFactory().fileSensitiveAction( performer, namePattern, icon );
103
    }
84
}
104
}

Return to bug 51571