KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > actions > CallbackSystemAction


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.util.actions;
21
22 import java.awt.Component JavaDoc;
23 import java.awt.Toolkit JavaDoc;
24 import java.awt.event.ActionEvent JavaDoc;
25 import java.beans.PropertyChangeEvent JavaDoc;
26 import java.beans.PropertyChangeListener JavaDoc;
27 import java.beans.PropertyChangeSupport JavaDoc;
28 import java.lang.ref.Reference JavaDoc;
29 import java.lang.ref.WeakReference JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Collections JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.logging.Level JavaDoc;
35 import java.util.logging.Logger JavaDoc;
36 import javax.swing.Action JavaDoc;
37 import javax.swing.ActionMap JavaDoc;
38 import org.openide.util.ContextAwareAction;
39 import org.openide.util.Lookup;
40 import org.openide.util.LookupListener;
41 import org.openide.util.Mutex;
42 import org.openide.util.Utilities;
43 import org.openide.util.WeakListeners;
44 import org.openide.util.WeakSet;
45
46 /** Action that can have a performer of the action attached to it at any time,
47 * or changed.
48 * The action will be automatically disabled
49 * when it has no performer.
50 * <p>Also may be made sensitive to changes in window focus.
51 * @author Ian Formanek, Jaroslav Tulach, Petr Hamernik
52 */

53 public abstract class CallbackSystemAction extends CallableSystemAction implements ContextAwareAction {
54     /** action performer */
55     private static final String JavaDoc PROP_ACTION_PERFORMER = "actionPerformer"; // NOI18N
56

57     /** a list of all actions that has survive focus change set to false */
58     private static final WeakSet<Class JavaDoc<? extends CallbackSystemAction>> notSurviving = new WeakSet<Class JavaDoc<? extends CallbackSystemAction>>(37);
59
60     /** a list of actions surviving focus change */
61     private static final WeakSet<Class JavaDoc<? extends CallbackSystemAction>> surviving = new WeakSet<Class JavaDoc<? extends CallbackSystemAction>>(37);
62
63     /** key to access listener */
64     private static final Object JavaDoc LISTENER = new Object JavaDoc();
65     static final long serialVersionUID = -6305817805474624653L;
66
67     /** logging */
68     private static final Logger JavaDoc err = Logger.getLogger(
69             "org.openide.util.actions.CallbackSystemAction"
70         ); // NOI18N
71

72     /** Initialize the action to have no performer.
73     */

74     protected void initialize() {
75         super.initialize();
76         updateEnabled();
77         setSurviveFocusChange(false);
78     }
79
80     /** Get the current action performer.
81     * @return the current action performer, or <code>null</code> if there is currently no performer
82     * @deprecated use TopComponent.getActionMap() as described in the javadoc
83     */

84     @Deprecated JavaDoc
85     public ActionPerformer getActionPerformer() {
86         return (ActionPerformer) getProperty(PROP_ACTION_PERFORMER);
87     }
88
89     /** Set the action performer.
90     * The specified value can be <code>null</code>, which means that the action will have no performer
91     * and is disabled. ({@link #isEnabled} will return <code>false</code> regardless its previous state.)
92     * <P>
93     * This method is <em>too dynamic</em> it depends on the actuall order of callers and
94     * is for example very fragile with respect to focus switching and correct delivering of
95     * focus change events. That is why an alternative based on
96     * <a HREF="http://openide.netbeans.org/proposals/actions/design.html#callback">ActionMap proposal</a>
97     * has been developed.
98     * <P>
99     * So if you are providing a <a HREF="@org-openide-windows@/org/openide/windows/TopComponent.html">TopComponent</a>
100     * and want to provide
101     * your own handling of <a HREF="@org-openide-actions@/org/openide/actions/CopyAction.html">CopyAction</a> use following code:
102     * <PRE>
103     * TopComponent tc = ...;
104     * javax.swing.Action yourCopyAction = ...; // the action to invoke instead of Copy
105     *
106     * CopyAction globalCopyAction = SystemAction.get (CopyAction.class);
107     * Object key = globalCopyAction.getActionMapKey(); // key is a special value defined by all CallbackSystemActions
108     *
109     * // and finally:
110     * tc.getActionMap ().put (key, yourCopyAction);
111     * </PRE>
112     * This code registers <code>yourCopyAction</code> with <code>tc</code>
113     * top component, so whenever a <code>globalCopyAction</code> is invoked,
114     * your action is being delegated to.
115     *
116     * @param performer the new action performer or <code>null</code> to disable
117     *
118     * @deprecated use TopComponent.getActionMap() as described in the javadoc
119     */

120     @Deprecated JavaDoc
121     public void setActionPerformer(ActionPerformer performer) {
122         putProperty(PROP_ACTION_PERFORMER, performer);
123         updateEnabled();
124     }
125
126     /** Updates the enabled state by checking performer and ActionMap
127      */

128     private void updateEnabled() {
129         Action JavaDoc action = GlobalManager.getDefault().findGlobalAction(
130                 getActionMapKey(), getSurviveFocusChange()
131             );
132
133         if (action != null) {
134             setEnabled(action.isEnabled());
135
136             synchronized (LISTENER) {
137                 ActionDelegateListener l = (ActionDelegateListener) getProperty(LISTENER);
138
139                 if ((l == null) || (l.get() != this)) {
140                     l = new ActionDelegateListener(this, action);
141                     putProperty(LISTENER, l);
142                 } else {
143                     l.attach(action);
144                 }
145             }
146         } else {
147             if (getActionPerformer() != null) {
148                 // we have performer
149
setEnabled(true);
150             } else {
151                 setEnabled(false);
152             }
153
154             clearListener();
155         }
156     }
157
158     /** Clears the listener.
159      */

160     private void clearListener() {
161         synchronized (LISTENER) {
162             // remove listener on any action
163
ActionDelegateListener l = (ActionDelegateListener) getProperty(LISTENER);
164
165             if (l != null) {
166                 l.clear();
167                 putProperty(LISTENER, null);
168             }
169         }
170     }
171
172     /** Perform the action. Tries the performer and then scans the ActionMap
173      * of selected topcomponent.
174      */

175     public void actionPerformed(final ActionEvent JavaDoc ev) {
176         // First try global context action.
177
final Action JavaDoc action = GlobalManager.getDefault().findGlobalAction(getActionMapKey(), getSurviveFocusChange());
178
179         if (action != null) {
180             if (action.isEnabled()) {
181                 action.actionPerformed(ev);
182             } else {
183                 Toolkit.getDefaultToolkit().beep();
184             }
185
186             return;
187         }
188
189         final Object JavaDoc ap = getActionPerformer();
190
191         if (ap != null) {
192             org.netbeans.modules.openide.util.ActionsBridge.doPerformAction(
193                 this,
194                 new org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable(ev, this, asynchronous ()) {
195                     public void run() {
196                         if (ap == getActionPerformer()) {
197                             getActionPerformer().performAction(CallbackSystemAction.this);
198                         }
199                     }
200                 }
201             );
202
203             return;
204         }
205
206         Toolkit.getDefaultToolkit().beep();
207     }
208
209     /** Perform the action.
210     * This default implementation calls the assigned action performer if it
211     * exists, otherwise does nothing.
212      * @deprecated This only uses {@link ActionPerformer}. Use {@link #actionPerformed} instead.
213     */

214     @Deprecated JavaDoc
215     public void performAction() {
216         ActionPerformer ap = getActionPerformer();
217
218         if (ap != null) {
219             ap.performAction(this);
220         }
221     }
222
223     /** Getter for action map key, which is used to find action from provided
224      * context (i.e. <code>ActionMap</code> provided by the context),
225      * which acts as a callback.
226      * Override this method in subclasses to provide 'nice' key.
227      * @return key which is used to find the action which performs callback,
228      * default returned key is a class name.
229      * @since 3.29 */

230     public Object JavaDoc getActionMapKey() {
231         return getClass().getName();
232     }
233
234     /** Test whether the action will survive a change in focus.
235     * By default, it will not.
236     * @return <code>true</code> if the enabled state of the action survives focus changes
237     */

238     public boolean getSurviveFocusChange() {
239         getProperty(null); // force initialization
240

241         return !notSurviving.contains(getClass());
242     }
243
244     /** Implements <code>ContextAwareAction</code> interface method. */
245     public Action JavaDoc createContextAwareInstance(Lookup actionContext) {
246         return new DelegateAction(this, actionContext);
247     }
248
249     /** Set whether the action will survive a change in focus.
250     * If <code>false</code>, then the action will be automatically
251     * disabled (using {@link #setActionPerformer}) when the window
252     * focus changes.
253     *
254     * @param b <code>true</code> to survive focus changes, <code>false</code> to be sensitive to them
255     */

256     public void setSurviveFocusChange(boolean b) {
257         synchronized (notSurviving) {
258             if (b) {
259                 notSurviving.remove(getClass());
260                 surviving.add(getClass());
261             } else {
262                 notSurviving.add(getClass());
263                 surviving.remove(getClass());
264             }
265         }
266     }
267
268     /** Array of actions from a set of classes.
269      */

270     private static List JavaDoc<CallbackSystemAction> toInstances(java.util.Set JavaDoc<Class JavaDoc<? extends CallbackSystemAction>> s) {
271         List JavaDoc<CallbackSystemAction> actions;
272
273         synchronized (notSurviving) {
274             actions = new ArrayList JavaDoc<CallbackSystemAction>(s.size());
275
276             for (Class JavaDoc<? extends CallbackSystemAction> c : s) {
277
278                 CallbackSystemAction a = SystemAction.findObject(c, false);
279
280                 if (a != null) {
281                     actions.add(a);
282                 }
283             }
284         }
285
286         return actions;
287     }
288
289     /** Clears all action performers for those that has setSurviveFocusChange
290      * on true.
291      */

292     private static void clearActionPerformers() {
293         List JavaDoc<CallbackSystemAction> actions = toInstances(notSurviving);
294
295         // clear the performers out of any loop
296
for (CallbackSystemAction a : actions) {
297             a.setActionPerformer(null);
298         }
299
300         actions = toInstances(surviving);
301
302         // clear the performers out of any loop
303
for (CallbackSystemAction a : actions) {
304
305             if (err.isLoggable(Level.FINE)) {
306                 err.fine("updateEnabled: " + a); // NOI18N
307
}
308
309             a.updateEnabled();
310         }
311     }
312
313     /** Listener on a global context.
314      */

315     private static final class GlobalManager implements LookupListener {
316         private static GlobalManager instance;
317         private Lookup.Result<ActionMap JavaDoc> result;
318         private Reference JavaDoc<ActionMap JavaDoc> actionMap = new WeakReference JavaDoc<ActionMap JavaDoc>(null);
319         private final ActionMap JavaDoc survive = new ActionMap JavaDoc();
320
321         private GlobalManager() {
322             result = Utilities.actionsGlobalContext().lookup(new Lookup.Template<ActionMap JavaDoc>(ActionMap JavaDoc.class));
323             result.addLookupListener(this);
324             resultChanged(null);
325         }
326
327         public synchronized static GlobalManager getDefault() {
328             if (instance != null) {
329                 return instance;
330             }
331
332             instance = new GlobalManager();
333
334             return instance;
335         }
336
337         public Action JavaDoc findGlobalAction(Object JavaDoc key, boolean surviveFocusChange) {
338             ActionMap JavaDoc map = actionMap.get();
339             Action JavaDoc a = (map == null) ? null : map.get(key);
340
341             if (surviveFocusChange) {
342                 if (a == null) {
343                     a = survive.get(key);
344
345                     if (a != null) {
346                         a = ((WeakAction) a).getDelegate();
347                     }
348
349                     if (err.isLoggable(Level.FINE)) {
350                         err.fine("No action for key: " + key + " using delegate: " + a); // NOI18N
351
}
352                 } else {
353                     if (err.isLoggable(Level.FINE)) {
354                         err.fine("New action for key: " + key + " put: " + a);
355                     }
356
357                     survive.put(key, new WeakAction(a));
358                 }
359             }
360
361             if (err.isLoggable(Level.FINE)) {
362                 err.fine("Action for key: " + key + " is: " + a); // NOI18N
363
}
364
365             return a;
366         }
367
368         /** Change all that do not survive ActionMap change */
369         public void resultChanged(org.openide.util.LookupEvent ev) {
370             ActionMap JavaDoc a = Utilities.actionsGlobalContext().lookup(ActionMap JavaDoc.class);
371
372             if (err.isLoggable(Level.FINE)) {
373                 err.fine("changed map : " + a); // NOI18N
374
err.fine("previous map: " + actionMap.get()); // NOI18N
375
}
376
377             if (a == actionMap.get()) {
378                 return;
379             }
380
381             actionMap = new WeakReference JavaDoc<ActionMap JavaDoc>(a);
382
383             if (err.isLoggable(Level.FINE)) {
384                 err.fine("clearActionPerformers"); // NOI18N
385
}
386
387             Mutex.EVENT.readAccess(new Runnable JavaDoc() {
388                 public void run() {
389                     clearActionPerformers();
390                 }
391             });
392         }
393     }
394      // end of LookupListener
395

396     /** An action that holds a weak reference to other action.
397      */

398     private static final class WeakAction extends WeakReference JavaDoc<Action JavaDoc> implements Action JavaDoc {
399         public WeakAction(Action JavaDoc delegate) {
400             super(delegate);
401         }
402
403         public Action JavaDoc getDelegate() {
404             return get();
405         }
406
407         public Object JavaDoc getValue(String JavaDoc key) {
408             throw new UnsupportedOperationException JavaDoc();
409         }
410
411         public void putValue(String JavaDoc key, Object JavaDoc value) {
412             throw new UnsupportedOperationException JavaDoc();
413         }
414
415         public void actionPerformed(ActionEvent JavaDoc e) {
416             throw new UnsupportedOperationException JavaDoc();
417         }
418
419         public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
420             throw new UnsupportedOperationException JavaDoc();
421         }
422
423         public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
424             throw new UnsupportedOperationException JavaDoc();
425         }
426
427         public void setEnabled(boolean b) {
428             throw new UnsupportedOperationException JavaDoc();
429         }
430
431         public boolean isEnabled() {
432             throw new UnsupportedOperationException JavaDoc();
433         }
434     }
435
436     /** A class that listens on changes in enabled state of an action
437      * and updates the state of the action according to it.
438      */

439     private static final class ActionDelegateListener extends WeakReference JavaDoc<CallbackSystemAction> implements PropertyChangeListener JavaDoc {
440         private Reference JavaDoc<Action JavaDoc> delegate;
441
442         public ActionDelegateListener(CallbackSystemAction c, Action JavaDoc delegate) {
443             super(c);
444             this.delegate = new WeakReference JavaDoc<Action JavaDoc>(delegate);
445             delegate.addPropertyChangeListener(this);
446         }
447
448         public void clear() {
449             Action JavaDoc a;
450
451             Reference JavaDoc<Action JavaDoc> d = delegate;
452             a = d == null ? null : d.get();
453
454             if (a == null) {
455                 return;
456             }
457
458             delegate = null;
459
460             a.removePropertyChangeListener(this);
461         }
462
463         public void attach(Action JavaDoc action) {
464             Reference JavaDoc<Action JavaDoc> d = delegate;
465
466             if ((d != null) && (d.get() == action)) {
467                 return;
468             }
469
470             Action JavaDoc prev = d.get();
471
472             // reattaches to different action
473
if (prev != null) {
474                 prev.removePropertyChangeListener(this);
475             }
476
477             this.delegate = new WeakReference JavaDoc<Action JavaDoc>(action);
478             action.addPropertyChangeListener(this);
479         }
480
481         public void propertyChange(java.beans.PropertyChangeEvent JavaDoc evt) {
482             synchronized (LISTENER) {
483                 Reference JavaDoc<Action JavaDoc> d = delegate;
484
485                 if ((d == null) || (d.get() == null)) {
486                     return;
487                 }
488             }
489
490             CallbackSystemAction c = get();
491
492             if (c != null) {
493                 c.updateEnabled();
494             }
495         }
496     }
497
498     /** A delegate action that is usually associated with a specific lookup and
499      * extract the nodes it operates on from it. Otherwise it delegates to the
500      * regular NodeAction.
501      */

502     private static final class DelegateAction extends Object JavaDoc implements Action JavaDoc,
503         LookupListener, Presenter.Menu, Presenter.Popup, Presenter.Toolbar, PropertyChangeListener JavaDoc {
504         /** action to delegate too */
505         private CallbackSystemAction delegate;
506
507         /** lookup we are associated with (or null) */
508         private Lookup.Result<ActionMap JavaDoc> result;
509
510         /** previous state of enabled */
511         private boolean enabled;
512
513         /** support for listeners */
514         private PropertyChangeSupport JavaDoc support = new PropertyChangeSupport JavaDoc(this);
515
516         /** listener to check listen on state of action(s) we delegate to */
517         private PropertyChangeListener JavaDoc weakL;
518
519         /** last action we were listening to */
520         private Reference JavaDoc<Action JavaDoc> lastRef;
521
522         public DelegateAction(CallbackSystemAction a, Lookup actionContext) {
523             this.delegate = a;
524             this.weakL = org.openide.util.WeakListeners.propertyChange(this, null);
525             this.enabled = a.getActionPerformer() != null;
526
527             this.result = actionContext.lookup(new Lookup.Template<ActionMap JavaDoc>(ActionMap JavaDoc.class));
528             this.result.addLookupListener(WeakListeners.create(LookupListener.class, this, this.result));
529             resultChanged(null);
530         }
531
532         /** Overrides superclass method, adds delegate description. */
533         public String JavaDoc toString() {
534             return super.toString() + "[delegate=" + delegate + "]"; // NOI18N
535
}
536
537         /** Invoked when an action occurs.
538          */

539         public void actionPerformed(final java.awt.event.ActionEvent JavaDoc e) {
540             final Action JavaDoc a = findAction();
541
542             if (a != null) {
543                 org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable run;
544                 run = new org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable(e, delegate, delegate.asynchronous()) {
545                             public void run() {
546                                 a.actionPerformed(e);
547                             }
548                         };
549
550                 org.netbeans.modules.openide.util.ActionsBridge.doPerformAction(delegate, run);
551             } else {
552                 // XXX #30303 if the action falls back to the old behaviour
553
// it may not be performed in case it is in dialog and
554
// is not transmodal.
555
// This is just a hack, see TopComponent.processKeyBinding.
556
Object JavaDoc source = e.getSource();
557
558                 if (
559                     source instanceof Component JavaDoc &&
560                         javax.swing.SwingUtilities.getWindowAncestor((Component JavaDoc) source) instanceof java.awt.Dialog JavaDoc
561                 ) {
562                     Object JavaDoc value = delegate.getValue("OpenIDE-Transmodal-Action"); // NOI18N
563

564                     if (!Boolean.TRUE.equals(value)) {
565                         return;
566                     }
567                 }
568
569                 delegate.actionPerformed(e);
570             }
571         }
572
573         public boolean isEnabled() {
574             Action JavaDoc a = findAction();
575
576             if (a == null) {
577                 a = delegate;
578             }
579
580             // 40915 - hold last action weakly
581
Action JavaDoc last = lastRef == null ? null : lastRef.get();
582
583             if (a != last) {
584                 if (last != null) {
585                     last.removePropertyChangeListener(weakL);
586                 }
587
588                 lastRef = new WeakReference JavaDoc<Action JavaDoc>(a);
589                 a.addPropertyChangeListener(weakL);
590             }
591
592             return a.isEnabled();
593         }
594
595         public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
596             support.addPropertyChangeListener(listener);
597         }
598
599         public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
600             support.removePropertyChangeListener(listener);
601         }
602
603         public void putValue(String JavaDoc key, Object JavaDoc o) {
604         }
605
606         public Object JavaDoc getValue(String JavaDoc key) {
607             return delegate.getValue(key);
608         }
609
610         public void setEnabled(boolean b) {
611         }
612
613         public void resultChanged(org.openide.util.LookupEvent ev) {
614             boolean newEnabled = isEnabled();
615
616             if (newEnabled != enabled) {
617                 support.firePropertyChange(PROP_ENABLED, enabled, newEnabled);
618                 enabled = newEnabled;
619             }
620         }
621
622         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
623             resultChanged(null);
624         }
625
626         /*** Finds an action that we should delegate to
627          * @return the action or null
628          */

629         private Action JavaDoc findAction() {
630             Collection JavaDoc<? extends ActionMap JavaDoc> c = result != null ? result.allInstances() : Collections.<ActionMap JavaDoc>emptySet();
631
632             if (!c.isEmpty()) {
633                 Object JavaDoc key = delegate.getActionMapKey();
634                 for (ActionMap JavaDoc map : c) {
635                     Action JavaDoc action = map.get(key);
636                     if (action != null) {
637                         return action;
638                     }
639                 }
640             }
641
642             return null;
643         }
644
645         public javax.swing.JMenuItem JavaDoc getMenuPresenter() {
646             if (isMethodOverridden(delegate, "getMenuPresenter")) { // NOI18N
647

648                 return delegate.getMenuPresenter();
649             } else {
650                 return org.netbeans.modules.openide.util.AWTBridge.getDefault().createMenuPresenter(this);
651             }
652         }
653
654         public javax.swing.JMenuItem JavaDoc getPopupPresenter() {
655             if (isMethodOverridden(delegate, "getPopupPresenter")) { // NOI18N
656

657                 return delegate.getPopupPresenter();
658             } else {
659                 return org.netbeans.modules.openide.util.AWTBridge.getDefault().createPopupPresenter(this);
660             }
661         }
662
663         public java.awt.Component JavaDoc getToolbarPresenter() {
664             if (isMethodOverridden(delegate, "getToolbarPresenter")) { // NOI18N
665

666                 return delegate.getToolbarPresenter();
667             } else {
668                 return org.netbeans.modules.openide.util.AWTBridge.getDefault().createToolbarPresenter(this);
669             }
670         }
671
672         private boolean isMethodOverridden(CallableSystemAction d, String JavaDoc name) {
673             try {
674                 java.lang.reflect.Method JavaDoc m = d.getClass().getMethod(name, new Class JavaDoc[0]);
675
676                 return m.getDeclaringClass() != CallableSystemAction.class;
677             } catch (java.lang.NoSuchMethodException JavaDoc ex) {
678                 ex.printStackTrace();
679                 throw new IllegalStateException JavaDoc("Error searching for method " + name + " in " + d); // NOI18N
680
}
681         }
682
683         protected void finalize() {
684             Action JavaDoc last = lastRef == null ? null : lastRef.get();
685
686             if (last != null) {
687                 last.removePropertyChangeListener(weakL);
688             }
689         }
690     }
691      // end of DelegateAction
692
}
693
Popular Tags