KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > swing > actions > TargetManager


1 /*
2  * $Id: TargetManager.java,v 1.2 2004/09/30 00:05:52 davidson1 Exp $
3  *
4  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
5  * Santa Clara, California 95054, U.S.A. All rights reserved.
6  */

7
8 package org.jdesktop.swing.actions;
9
10 import java.awt.Component JavaDoc;
11
12 import java.awt.event.ActionEvent JavaDoc;
13
14 import java.beans.PropertyChangeEvent JavaDoc;
15 import java.beans.PropertyChangeListener JavaDoc;
16 import java.beans.PropertyChangeSupport JavaDoc;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.EventObject JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22
23 import javax.swing.Action JavaDoc;
24 import javax.swing.ActionMap JavaDoc;
25 import javax.swing.JComponent JavaDoc;
26 import javax.swing.FocusManager JavaDoc;
27
28 import org.jdesktop.swing.Application;
29
30
31 /**
32  * The target manager dispatches commands to {@link Targetable} objects
33  * that it manages. This design of this class is based on the <i>Chain of
34  * Responsiblity</i> and <i>Mediator</i> design patterns. The target manager
35  * acts as a mediator between {@link TargetableAction}s and the intended targets.
36  * This allows Action based components to invoke commands on components
37  * without explicitly binding the user Action to the component action.
38  * <p>
39  * The target manager maintains a reference to a current
40  * target and a target list.
41  * The target list is managed using the <code>addTarget</code> and
42  * <code>removeTarget</code> methods. The current target is managed using the
43  * <code>setTarget</code> and <code>getTarget</code> methods.
44  * <p>
45  * Commands are dispatched to the Targetable objects in the <code>doCommand</code>
46  * method in a well defined order. The doCommand method on the Targetable object
47  * is called and if it returns true then the command has been handled and
48  * command dispatching will stop. If the Targetable doCommand method returns
49  * false then the
50  * <p>
51  * If none of the Targetable objects can handle the command then the default
52  * behaviour is to retrieve an Action from the {@link javax.swing.ActionMap} of
53  * the permanent focus owner with a key that matches the command key. If an
54  * Action can be found thenthe <code>actionPerformed</code>
55  * method is invoked using an <code>ActionEvent</code> that was constructed
56  * using the command string.
57  * <p>
58  * If the Action is not found on the focus order then the ActionMaps of the ancestor
59  * hierarchy of the focus owner is searched until a matching Action can be found.
60  * Finally, if none
61  * of the components can handle the command then it is dispatche to the ActionMap
62  * of the current Application instance.
63  * <p>
64  * The order of command dispatch is as follows:
65  * <ul>
66  * <li>Current Targetable object invoking doCommand method
67  * <li>List order of Targetable objects invoking doCommand method
68  * <li>ActionMap entry of the permanent focus owner invoking actionPerfomed
69  * <li>ActionMap entry of the ancestor hierarchy of the permanent focus owner
70  * <li>ActionMap entry of the current Application instance
71  * </ul>
72  *
73  * @see Targetable
74  * @see TargetableAction
75  * @author Mark Davidson
76  */

77 public class TargetManager {
78
79     private static TargetManager INSTANCE;
80     private List JavaDoc targetList;
81     private Targetable target;
82     private PropertyChangeSupport JavaDoc propertySupport;
83
84     /**
85      * Create a target manager. Use this constructor if the application
86      * may support many target managers. Otherwise, using the getInstance method
87      * will return a singleton.
88      */

89     public TargetManager() {
90         propertySupport = new PropertyChangeSupport JavaDoc(this);
91     }
92
93     /**
94      * Return the singleton instance.
95      */

96     public static TargetManager getInstance() {
97         if (INSTANCE == null) {
98             INSTANCE = new TargetManager();
99         }
100         return INSTANCE;
101     }
102
103     /**
104      * Add a target to the target list. Will be appended
105      * to the list by default. If the prepend flag is true then
106      * the target will be added at the head of the list.
107      * <p>
108      * Targets added to the head of the list will will be the first
109      * to handle the command.
110      *
111      * @param target the targeted object to add
112      * @param prepend if true add at the head of the list; false append
113      */

114     public void addTarget(Targetable target, boolean prepend) {
115         if (targetList == null) {
116             targetList = new ArrayList JavaDoc();
117         }
118         if (prepend) {
119             targetList.add(0, target);
120         } else {
121             targetList.add(target);
122         }
123         // Should add focus listener to the component.
124
}
125
126     /**
127      * Appends the target to the target list.
128      * @param target the targeted object to add
129      */

130     public void addTarget(Targetable target) {
131         addTarget(target, false);
132     }
133
134     /**
135      * Remove the target from the list
136      */

137     public void removeTarget(Targetable target) {
138         if (targetList != null) {
139             targetList.remove(target);
140         }
141     }
142
143     /**
144      * Returns an array of managed targets that were added with the
145      * <code>addTarget</code> methods.
146      *
147      * @return all the <code>Targetable</code> added or an empty array if no
148      * targets have been added
149      */

150     public Targetable[] getTargets() {
151         Targetable[] targets;
152         if (targetList == null) {
153             targets = new Targetable[0];
154         } else {
155             targets = new Targetable[targetList.size()];
156             targets = (Targetable[])targetList.toArray(new Targetable[targetList.size()]);
157         }
158         return targets;
159     }
160
161     /**
162      * Gets the current targetable component. May or may not
163      * in the target list. If the current target is null then
164      * the the current targetable component will be the first one
165      * in the target list which can execute the command.
166      *
167      * This is a bound property and will fire a property change event
168      * if the value changes.
169      *
170      * @param newTarget the current targetable component to set or null if
171      * the TargetManager shouldn't have a current targetable component.
172      */

173     public void setTarget(Targetable newTarget) {
174         Targetable oldTarget = target;
175         if (oldTarget != newTarget) {
176             target = newTarget;
177             propertySupport.firePropertyChange("target", oldTarget, newTarget);
178         }
179     }
180
181     /**
182      * Return the current targetable component. The curent targetable component
183      * is the first place where commands will be dispatched.
184      *
185      * @return the current targetable component or null
186      */

187     public Targetable getTarget() {
188         return target;
189     }
190
191     public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
192         propertySupport.addPropertyChangeListener(listener);
193     }
194
195     public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
196         propertySupport.removePropertyChangeListener(listener);
197     }
198
199     /**
200      * Listens to changes in the target property, disables managed actions
201      *
202      class TargetListener implements PropertyChangeListener {
203      public void propertyChange(PropertyChangeEvent evt) {
204      ActionManager manager = ActionManager.getInstance();
205
206      // Disable old commands.
207      Targetable target = (Targetable)evt.getOldValue();
208      if (target != null) {
209      String[] commands = target.getCommands();
210      for (int i = 0; i < commands.length; i++) {
211      manager.setEnabled(commands[i], false);
212      }
213      }
214
215      // Enable new commands
216      target = (Targetable)evt.getNewValue();
217      if (target != null) {
218      String[] commands = target.getCommands();
219      for (int i = 0; i < commands.length; i++) {
220      manager.setEnabled(commands[i], true);
221      }
222      }
223      }
224      }*/

225
226     /**
227      * Executes the command on the current targetable component.
228      * If there isn't current targetable component then the list
229      * of targetable components are searched and the first component
230      * which can execute the command. If none of the targetable
231      * components handle the command then the ActionMaps of the
232      * focused components are searched.
233      *
234      * @param command the key of the command
235      * @param value the value of the command; depends on context
236      * @return true if the command has been handled otherwise false
237      */

238     public boolean doCommand(Object JavaDoc command, Object JavaDoc value) {
239         // Try to invoked the explicit target.
240
if (target != null) {
241             if (target.hasCommand(command) && target.doCommand(command, value)) {
242                 return true;
243             }
244         }
245
246         // The target list has the next chance to handle the command.
247
if (targetList != null) {
248             Iterator JavaDoc iter = targetList.iterator();
249             while (iter.hasNext()) {
250                 Targetable target = (Targetable)iter.next();
251                 if (target.hasCommand(command) &&
252                     target.doCommand(command, value)) {
253                     return true;
254                 }
255             }
256         }
257
258         ActionEvent JavaDoc evt = null;
259         if (value instanceof ActionEvent JavaDoc) {
260             evt = (ActionEvent JavaDoc)value;
261         }
262
263         // Fall back behavior. Get the component which has focus and search the
264
// ActionMaps in the containment hierarchy for matching action.
265
Component JavaDoc comp = FocusManager.getCurrentManager().getPermanentFocusOwner();
266         while (comp != null) {
267             if (comp instanceof JComponent JavaDoc) {
268                 ActionMap JavaDoc map = ((JComponent JavaDoc)comp).getActionMap();
269                 Action JavaDoc action = map.get(command);
270                 if (action != null) {
271                     if (evt == null) {
272                         evt = new ActionEvent JavaDoc(comp, 0, command.toString());
273                     }
274                     action.actionPerformed(evt);
275
276                     return true;
277                 }
278             }
279             comp = comp.getParent();
280         }
281
282         Application app = Application.getInstance();
283         if (app != null) {
284             ActionMap JavaDoc map = app.getActionMap();
285             Action JavaDoc action = map.get(command);
286             if (action != null) {
287                 if (evt == null) {
288                     evt = new ActionEvent JavaDoc(comp, 0, command.toString());
289                 }
290                 action.actionPerformed(evt);
291                 return true;
292             }
293         }
294         return false;
295     }
296
297     /**
298      * Resets the TargetManager.
299      * This method is package private and for testing purposes only.
300      */

301     void reset() {
302         if (targetList != null) {
303             targetList.clear();
304             targetList = null;
305         }
306         target = null;
307
308         PropertyChangeListener JavaDoc[] listeners = propertySupport.getPropertyChangeListeners();
309         for (int i = 0; i < listeners.length; i++) {
310             propertySupport.removePropertyChangeListener(listeners[i]);
311         }
312         INSTANCE = null;
313     }
314
315 }
316
Popular Tags