KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > actions > ToolsAction


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.actions;
20
21 import org.openide.awt.Actions;
22 import org.openide.util.ContextAwareAction;
23 import org.openide.util.HelpCtx;
24 import org.openide.util.Lookup;
25 import org.openide.util.NbBundle;
26 import org.openide.util.actions.*;
27
28 import java.beans.*;
29
30 import java.util.*;
31
32 import javax.swing.*;
33 import javax.swing.event.*;
34 import org.openide.awt.DynamicMenuContent;
35
36
37 /** A "meta-action" that displays (in a submenu) a list of enabled actions provided by modules.
38 * Such registered actions are called "service actions":
39 * they are provided externally but seem to provide additional services on existing components.
40 * Often they will be {@link NodeAction}s or {@link CookieAction}s so that they will
41 * be enabled based on the node selection, i.e. the node containing this popup.
42 * It is desirable for most nodes to include this action somewhere in their popup menu.
43 *
44 * <p><em>Note:</em> you do not need to touch this class to add a service action!
45 * Just add the action to a module manifest in an <code>Action</code> section.
46 *
47 * <p>The list of registered service actions is provided to this action from the implementation
48 * by means of {@link ActionManager}.
49 *
50 * @author Jaroslav Tulach
51 */

52 public class ToolsAction extends SystemAction implements ContextAwareAction, Presenter.Menu, Presenter.Popup {
53     static final long serialVersionUID = 4906417339959070129L;
54
55     // Global ActionManager listener monitoring all available actions
56
// and their state
57
private static G gl;
58
59     /** Lazy initialization of global listener.
60      */

61     private static synchronized G gl() {
62         if (gl == null) {
63             gl = new G();
64         }
65
66         return gl;
67     }
68
69     /* @return name
70     */

71     public String JavaDoc getName() {
72         return getActionName();
73     }
74
75     /* @return help for this action
76     */

77     public HelpCtx getHelpCtx() {
78         return new HelpCtx(ToolsAction.class);
79     }
80
81     /* @return menu presenter for the action
82     */

83     public JMenuItem getMenuPresenter() {
84         return new Inline(this);
85     }
86
87     /* @return menu presenter for the action
88     */

89     public JMenuItem getPopupPresenter() {
90         return new Popup(this);
91     }
92
93     /* Does nothing.
94     */

95     public void actionPerformed(java.awt.event.ActionEvent JavaDoc ev) {
96         assert false;
97     }
98
99     public Action createContextAwareInstance(Lookup actionContext) {
100         return new DelegateAction(this, actionContext);
101     }
102
103     /* @return name
104     */

105     private static String JavaDoc getActionName() {
106         return NbBundle.getMessage(ToolsAction.class, "CTL_Tools");
107     }
108
109     /** Implementation method that regenerates the items in the menu or
110     * in the array.
111     *
112     * @param forMenu true if will be presented in menu or false if presented in popup
113     * @param list (can be null)
114     */

115     private static List generate(Action toolsAction, boolean forMenu) {
116         ActionManager am = ActionManager.getDefault();
117         SystemAction[] actions = am.getContextActions();
118         List list = new ArrayList(actions.length);
119
120         boolean separator = false;
121         boolean firstItemAdded = false; // flag to prevent adding separator before actual menu items
122

123         // Get action context.
124
Lookup lookup;
125
126         if (toolsAction instanceof Lookup.Provider) {
127             lookup = ((Lookup.Provider) toolsAction).getLookup();
128         } else {
129             lookup = null;
130         }
131
132         for (int i = 0; i < actions.length; i++) {
133             Action a;
134
135             // Retrieve context sensitive action instance if possible.
136
if ((lookup != null) && actions[i] instanceof ContextAwareAction) {
137                 a = ((ContextAwareAction) actions[i]).createContextAwareInstance(lookup);
138             } else {
139                 a = actions[i];
140             }
141
142             if (a == null) {
143                 if (firstItemAdded) {
144                     separator = true;
145                 }
146             } else {
147                 boolean isPopup = (a instanceof Presenter.Popup);
148                 boolean isMenu = (a instanceof Presenter.Menu);
149
150                 if (!((forMenu && isMenu) || (!forMenu && isPopup)) && (isMenu || isPopup)) {
151                     continue; // do not call isEnabled on action that is only popup presenter when building menu (i18nPopupAction)
152
}
153
154                 if (a.isEnabled()) {
155                     JMenuItem mi;
156
157                     if (forMenu && isMenu) {
158                         mi = ((Presenter.Menu) a).getMenuPresenter();
159                     } else if (!forMenu && isPopup) {
160                         mi = ((Presenter.Popup) a).getPopupPresenter();
161                     } else if (!isMenu && !isPopup) {
162                         // Generic Swing action.
163
mi = new JMenuItem();
164                         Actions.connect(mi, a, !forMenu);
165                     } else {
166                         // Should not be here.
167
continue;
168                     }
169
170                     if (separator) {
171                         list.add(null);
172                         separator = false;
173                     }
174
175                     list.add(mi);
176                     firstItemAdded = true;
177                 }
178             }
179         }
180
181         return list;
182     }
183
184     //------------------------------------------
185

186     /** @deprecated Useless, see {@link ActionManager}. */
187     public static void setModel(Model m) {
188         throw new SecurityException JavaDoc();
189     }
190
191     /** @deprecated Useless, see {@link ActionManager}. */
192     public static interface Model {
193         public SystemAction[] getActions();
194
195         public void addChangeListener(javax.swing.event.ChangeListener JavaDoc l);
196
197         public void removeChangeListener(javax.swing.event.ChangeListener JavaDoc l);
198     }
199
200     /** Inline menu that watches model changes only when really needed.
201      */

202     private static final class Inline extends JMenuItem implements DynamicMenuContent {
203         static final long serialVersionUID = 2269006599727576059L;
204
205         /** timestamp of the beginning of the last regeneration */
206         private int timestamp = 0;
207
208         /** Associated tools action. */
209         private Action toolsAction;
210
211         Inline(Action toolsAction) {
212             this.toolsAction = toolsAction;
213         }
214
215
216         
217         public JComponent[] synchMenuPresenters(JComponent[] items) {
218             if (timestamp == gl().getTimestamp()) {
219                 return items;
220             }
221             // generate directly list of menu items
222
List l = generate(toolsAction, true);
223             timestamp = gl().getTimestamp();
224             return (JMenuItem[]) l.toArray(new JMenuItem[l.size()]);
225         }
226         
227         
228         public JComponent[] getMenuPresenters() {
229             return synchMenuPresenters(new JComponent[0]);
230         }
231     }
232
233     //--------------------------------------------------
234

235     /** Inline menu that is either empty or contains one submenu.*/
236     private static final class Popup extends JMenuItem implements DynamicMenuContent {
237         static final long serialVersionUID = 2269006599727576059L;
238
239         /** sub menu */
240         private JMenu menu = new MyMenu();
241
242         /** Associated tools action. */
243         private Action toolsAction;
244
245         public Popup(Action toolsAction) {
246             super();
247             this.toolsAction = toolsAction;
248             HelpCtx.setHelpIDString(menu, ToolsAction.class.getName());
249
250         }
251
252         
253         public JComponent[] synchMenuPresenters(JComponent[] items) {
254             return gl().isPopupEnabled(toolsAction) ? new JMenuItem[] { menu } : new JMenuItem[0];
255         }
256         
257         
258         public JComponent[] getMenuPresenters() {
259             return synchMenuPresenters(new JComponent[0]);
260         }
261
262
263         /** A special menu that will properly update its submenu before posting */
264         private class MyMenu extends org.openide.awt.JMenuPlus implements PopupMenuListener {
265             /* A popup menu we've attached our listener to.
266              * If null, the content is not up-to-date */

267             private JPopupMenu lastPopup = null;
268
269             MyMenu() {
270                 super(getActionName());
271             }
272
273             public JPopupMenu getPopupMenu() {
274                 JPopupMenu popup = super.getPopupMenu();
275                 fillSubmenu(popup);
276
277                 return popup;
278             }
279
280             private void fillSubmenu(JPopupMenu pop) {
281                 if (lastPopup == null) {
282                     pop.addPopupMenuListener(this);
283                     lastPopup = pop;
284
285                     removeAll();
286
287                     Iterator it = generate(toolsAction, false).iterator();
288
289                     while (it.hasNext()) {
290                         java.awt.Component JavaDoc item = (java.awt.Component JavaDoc) it.next();
291
292                         if (item == null) {
293                             addSeparator();
294                         } else {
295                             add(item);
296                         }
297                     }
298
299                     // also work with empty element
300
if (getMenuComponentCount() == 0) {
301                         JMenuItem empty = new JMenuItem(NbBundle.getMessage(ToolsAction.class, "CTL_EmptySubMenu"));
302                         empty.setEnabled(false);
303                         add(empty);
304                     }
305                 }
306             }
307
308             public void popupMenuCanceled(PopupMenuEvent e) {
309             }
310
311             public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
312             }
313
314             public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
315                 lastPopup.removePopupMenuListener(this);
316                 lastPopup = null; // clear the status and stop listening
317
}
318         }
319     }
320
321     //------------------------------------------------
322
//----------------------------------------------------------
323
private static class G implements PropertyChangeListener {
324         public static final String JavaDoc PROP_STATE = "actionsState"; // NOI18N
325
private int timestamp = 1;
326         private SystemAction[] actions = null;
327         private PropertyChangeSupport supp = new PropertyChangeSupport(this);
328
329         public G() {
330             ActionManager am = ActionManager.getDefault();
331             am.addPropertyChangeListener(this);
332             actionsListChanged();
333         }
334
335         public final void addPropertyChangeListener(PropertyChangeListener listener) {
336             supp.addPropertyChangeListener(listener);
337         }
338
339         public final void removePropertyChangeListener(PropertyChangeListener listener) {
340             supp.removePropertyChangeListener(listener);
341         }
342
343         protected final void firePropertyChange(String JavaDoc name, Object JavaDoc o, Object JavaDoc n) {
344             supp.firePropertyChange(name, o, n);
345         }
346
347         private void actionsListChanged() {
348             timestamp++;
349
350             // deregister all actions listeners
351
SystemAction[] copy = actions;
352
353             if (copy != null) {
354                 for (int i = 0; i < copy.length; i++) {
355                     SystemAction act = copy[i];
356
357                     if (act != null) {
358                         act.removePropertyChangeListener(this);
359                     }
360                 }
361             }
362
363             ActionManager am = ActionManager.getDefault();
364             copy = am.getContextActions();
365
366             for (int i = 0; i < copy.length; i++) {
367                 SystemAction act = copy[i];
368
369                 if (act != null) {
370                     act.addPropertyChangeListener(this);
371                 }
372             }
373
374             actions = copy;
375
376             firePropertyChange(PROP_STATE, null, null); // tell the world
377
}
378
379         private void actionStateChanged() {
380             timestamp++;
381             firePropertyChange(PROP_STATE, null, null); // tell the world
382
}
383
384         public void propertyChange(PropertyChangeEvent ev) {
385             String JavaDoc prop = ev.getPropertyName();
386
387             if ((prop == null) || prop.equals(ActionManager.PROP_CONTEXT_ACTIONS)) {
388                 actionsListChanged();
389             } else if (prop.equals(SystemAction.PROP_ENABLED)) {
390                 actionStateChanged();
391             }
392         }
393
394         /** Tells if there is any action that is willing to provide
395          * Presenter.Popup
396          */

397         private boolean isPopupEnabled(Action toolsAction) {
398             boolean en = false;
399             SystemAction[] copy = actions;
400
401             // Get action conext.
402
Lookup lookup;
403
404             if (toolsAction instanceof Lookup.Provider) {
405                 lookup = ((Lookup.Provider) toolsAction).getLookup();
406             } else {
407                 lookup = null;
408             }
409
410             for (int i = 0; i < copy.length; i++) {
411                 // Get context aware action instance if needed.
412
Action act;
413
414                 // Retrieve context aware action instance if possible.
415
if ((lookup != null) && copy[i] instanceof ContextAwareAction) {
416                     act = ((ContextAwareAction) copy[i]).createContextAwareInstance(lookup);
417                 } else {
418                     act = copy[i];
419                 }
420
421                 if (act instanceof Presenter.Popup && act.isEnabled()) {
422                     en = true;
423
424                     break;
425                 }
426             }
427
428             return en;
429         }
430
431         private int getTimestamp() {
432             return timestamp;
433         }
434     }
435
436     /** Delegate tools action. Which act accordingly to current context
437      * (represented by lookup). */

438     private static final class DelegateAction extends Object JavaDoc implements Action, Presenter.Menu, Presenter.Popup,
439         Lookup.Provider {
440         private ToolsAction delegate;
441         private Lookup lookup;
442
443         /** support for listeners */
444         private PropertyChangeSupport support = new PropertyChangeSupport(this);
445
446         public DelegateAction(ToolsAction delegate, Lookup actionContext) {
447             this.delegate = delegate;
448             this.lookup = actionContext;
449         }
450
451         /** Overrides superclass method, adds delegate description. */
452         public String JavaDoc toString() {
453             return super.toString() + "[delegate=" + delegate + "]"; // NOI18N
454
}
455
456         /** Implements <code>Lookup.Provider</code>. */
457         public Lookup getLookup() {
458             return lookup;
459         }
460
461         public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
462         }
463
464         public void putValue(String JavaDoc key, Object JavaDoc o) {
465         }
466
467         public Object JavaDoc getValue(String JavaDoc key) {
468             return delegate.getValue(key);
469         }
470
471         public boolean isEnabled() {
472             // Irrelevant see G#isPopupEnabled(..).
473
return delegate.isEnabled();
474         }
475
476         public void setEnabled(boolean b) {
477             // Irrelevant see G#isPopupEnabled(..).
478
}
479
480         public void addPropertyChangeListener(PropertyChangeListener listener) {
481             support.addPropertyChangeListener(listener);
482         }
483
484         public void removePropertyChangeListener(PropertyChangeListener listener) {
485             support.removePropertyChangeListener(listener);
486         }
487
488         /** Implements <code>Presenter.Menu</code>. */
489         public javax.swing.JMenuItem JavaDoc getMenuPresenter() {
490             return new Inline(this);
491         }
492
493         /** Implements <code>Presenter.Popup</code>. */
494         public javax.swing.JMenuItem JavaDoc getPopupPresenter() {
495             return new ToolsAction.Popup(this);
496         }
497     }
498      // End of DelegateAction.
499
}
500
Popular Tags