KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > action > ExternalActionManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jface.action;
13
14 import java.text.MessageFormat JavaDoc; // Not using ICU to support standalone JFace scenario
15
import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.ResourceBundle JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.core.commands.Command;
23 import org.eclipse.core.commands.CommandEvent;
24 import org.eclipse.core.commands.CommandManager;
25 import org.eclipse.core.commands.ICommandListener;
26 import org.eclipse.core.commands.ParameterizedCommand;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Status;
29 import org.eclipse.jface.bindings.BindingManager;
30 import org.eclipse.jface.bindings.BindingManagerEvent;
31 import org.eclipse.jface.bindings.IBindingManagerListener;
32 import org.eclipse.jface.bindings.Trigger;
33 import org.eclipse.jface.bindings.TriggerSequence;
34 import org.eclipse.jface.bindings.keys.KeySequence;
35 import org.eclipse.jface.bindings.keys.KeyStroke;
36 import org.eclipse.jface.bindings.keys.SWTKeySupport;
37 import org.eclipse.jface.util.IPropertyChangeListener;
38 import org.eclipse.jface.util.Policy;
39 import org.eclipse.jface.util.PropertyChangeEvent;
40 import org.eclipse.jface.util.Util;
41
42 /**
43  * <p>
44  * A manager for a callback facility which is capable of querying external
45  * interfaces for additional information about actions and action contribution
46  * items. This information typically includes things like accelerators and
47  * textual representations.
48  * </p>
49  * <p>
50  * <em>It is only necessary to use this mechanism if you will be using a mix of
51  * actions and commands, and wish the interactions to work properly.</em>
52  * </p>
53  * <p>
54  * For example, in the Eclipse workbench, this mechanism is used to allow the
55  * command architecture to override certain values in action contribution items.
56  * </p>
57  * <p>
58  * This class is not intended to be called or extended by any external clients.
59  * </p>
60  *
61  * @since 3.0
62  */

63 public final class ExternalActionManager {
64
65     /**
66      * A simple implementation of the <code>ICallback</code> mechanism that
67      * simply takes a <code>BindingManager</code> and a
68      * <code>CommandManager</code>.
69      *
70      * @since 3.1
71      */

72     public static final class CommandCallback implements
73             IBindingManagerListener, IBindingManagerCallback {
74
75         /**
76          * The internationalization bundle for text produced by this class.
77          */

78         private static final ResourceBundle JavaDoc RESOURCE_BUNDLE = ResourceBundle
79                 .getBundle(ExternalActionManager.class.getName());
80
81         /**
82          * The callback capable of responding to whether a command is active.
83          */

84         private final IActiveChecker activeChecker;
85
86         /**
87          * The binding manager for your application. Must not be
88          * <code>null</code>.
89          */

90         private final BindingManager bindingManager;
91
92         /**
93          * Whether a listener has been attached to the binding manager yet.
94          */

95         private boolean bindingManagerListenerAttached = false;
96
97         /**
98          * The command manager for your application. Must not be
99          * <code>null</code>.
100          */

101         private final CommandManager commandManager;
102
103         /**
104          * A set of all the command identifiers that have been logged as broken
105          * so far. For each of these, there will be a listener on the
106          * corresponding command. If the command ever becomes defined, the item
107          * will be removed from this set and the listener removed. This value
108          * may be empty, but never <code>null</code>.
109          */

110         private final Set JavaDoc loggedCommandIds = new HashSet JavaDoc();
111
112         /**
113          * The list of listeners that have registered for property change
114          * notification. This is a map of command identifiers (<code>String</code>)
115          * to listeners (<code>IPropertyChangeListener</code>).
116          */

117         private final Map JavaDoc registeredListeners = new HashMap JavaDoc();
118
119         /**
120          * Constructs a new instance of <code>CommandCallback</code> with the
121          * workbench it should be using. All commands will be considered active.
122          *
123          * @param bindingManager
124          * The binding manager which will provide the callback; must
125          * not be <code>null</code>.
126          * @param commandManager
127          * The command manager which will provide the callback; must
128          * not be <code>null</code>.
129          *
130          * @since 3.1
131          */

132         public CommandCallback(final BindingManager bindingManager,
133                 final CommandManager commandManager) {
134             this(bindingManager, commandManager, new IActiveChecker() {
135                 public boolean isActive(String JavaDoc commandId) {
136                     return true;
137                 }
138
139             });
140         }
141
142         /**
143          * Constructs a new instance of <code>CommandCallback</code> with the
144          * workbench it should be using.
145          *
146          * @param bindingManager
147          * The binding manager which will provide the callback; must
148          * not be <code>null</code>.
149          * @param commandManager
150          * The command manager which will provide the callback; must
151          * not be <code>null</code>.
152          * @param activeChecker
153          * The callback mechanism for checking whether a command is
154          * active; must not be <code>null</code>.
155          *
156          * @since 3.1
157          */

158         public CommandCallback(final BindingManager bindingManager,
159                 final CommandManager commandManager,
160                 final IActiveChecker activeChecker) {
161             if (bindingManager == null) {
162                 throw new NullPointerException JavaDoc(
163                         "The callback needs a binding manager"); //$NON-NLS-1$
164
}
165
166             if (commandManager == null) {
167                 throw new NullPointerException JavaDoc(
168                         "The callback needs a command manager"); //$NON-NLS-1$
169
}
170
171             if (activeChecker == null) {
172                 throw new NullPointerException JavaDoc(
173                         "The callback needs an active callback"); //$NON-NLS-1$
174
}
175
176             this.activeChecker = activeChecker;
177             this.bindingManager = bindingManager;
178             this.commandManager = commandManager;
179         }
180
181         /**
182          * @see org.eclipse.jface.action.ExternalActionManager.ICallback#addPropertyChangeListener(String,
183          * IPropertyChangeListener)
184          */

185         public final void addPropertyChangeListener(final String JavaDoc commandId,
186                 final IPropertyChangeListener listener) {
187             registeredListeners.put(commandId, listener);
188             if (!bindingManagerListenerAttached) {
189                 bindingManager.addBindingManagerListener(this);
190                 bindingManagerListenerAttached = true;
191             }
192         }
193
194         public final void bindingManagerChanged(final BindingManagerEvent event) {
195             if (event.isActiveBindingsChanged()) {
196                 final Iterator JavaDoc listenerItr = registeredListeners.entrySet()
197                         .iterator();
198                 while (listenerItr.hasNext()) {
199                     final Map.Entry JavaDoc entry = (Map.Entry JavaDoc) listenerItr.next();
200                     final String JavaDoc commandId = (String JavaDoc) entry.getKey();
201                     final Command command = commandManager
202                             .getCommand(commandId);
203                     final ParameterizedCommand parameterizedCommand = new ParameterizedCommand(
204                             command, null);
205                     if (event.isActiveBindingsChangedFor(parameterizedCommand)) {
206                         final IPropertyChangeListener listener = (IPropertyChangeListener) entry
207                                 .getValue();
208                         listener.propertyChange(new PropertyChangeEvent(event
209                                 .getManager(), IAction.TEXT, null, null));
210                     }
211                 }
212             }
213         }
214
215         /**
216          * @see org.eclipse.jface.action.ExternalActionManager.ICallback#getAccelerator(String)
217          */

218         public final Integer JavaDoc getAccelerator(final String JavaDoc commandId) {
219             final TriggerSequence triggerSequence = bindingManager
220                     .getBestActiveBindingFor(commandId);
221             if (triggerSequence != null) {
222                 final Trigger[] triggers = triggerSequence.getTriggers();
223                 if (triggers.length == 1) {
224                     final Trigger trigger = triggers[0];
225                     if (trigger instanceof KeyStroke) {
226                         final KeyStroke keyStroke = (KeyStroke) trigger;
227                         final int accelerator = SWTKeySupport
228                                 .convertKeyStrokeToAccelerator(keyStroke);
229                         return new Integer JavaDoc(accelerator);
230                     }
231                 }
232             }
233
234             return null;
235         }
236
237         /**
238          * @see org.eclipse.jface.action.ExternalActionManager.ICallback#getAcceleratorText(String)
239          */

240         public final String JavaDoc getAcceleratorText(final String JavaDoc commandId) {
241             final TriggerSequence triggerSequence = bindingManager
242                     .getBestActiveBindingFor(commandId);
243             if (triggerSequence == null) {
244                 return null;
245             }
246
247             return triggerSequence.format();
248         }
249
250         /**
251          * Returns the active bindings for a particular command identifier.
252          *
253          * @param commandId
254          * The identifier of the command whose bindings are
255          * requested. This argument may be <code>null</code>. It
256          * is assumed that the command has no parameters.
257          * @return The array of active triggers (<code>TriggerSequence</code>)
258          * for a particular command identifier. This value is guaranteed
259          * not to be <code>null</code>, but it may be empty.
260          * @since 3.2
261          */

262         public final TriggerSequence[] getActiveBindingsFor(
263                 final String JavaDoc commandId) {
264             return bindingManager.getActiveBindingsFor(commandId);
265         }
266
267         /**
268          * @see org.eclipse.jface.action.ExternalActionManager.ICallback#isAcceleratorInUse(int)
269          */

270         public final boolean isAcceleratorInUse(final int accelerator) {
271             final KeySequence keySequence = KeySequence
272                     .getInstance(SWTKeySupport
273                             .convertAcceleratorToKeyStroke(accelerator));
274             return bindingManager.isPerfectMatch(keySequence)
275                     || bindingManager.isPartialMatch(keySequence);
276         }
277
278         /**
279          * {@inheritDoc}
280          *
281          * Calling this method with an undefined command id will generate a log
282          * message.
283          */

284         public final boolean isActive(final String JavaDoc commandId) {
285             if (commandId != null) {
286                 final Command command = commandManager.getCommand(commandId);
287
288                 if (!command.isDefined()
289                         && (!loggedCommandIds.contains(commandId))) {
290                     // The command is not yet defined, so we should log this.
291
final String JavaDoc message = MessageFormat.format(Util
292                             .translateString(RESOURCE_BUNDLE,
293                                     "undefinedCommand.WarningMessage", null), //$NON-NLS-1$
294
new String JavaDoc[] { command.getId() });
295                     IStatus status = new Status(IStatus.ERROR,
296                             "org.eclipse.jface", //$NON-NLS-1$
297
0, message, new Exception JavaDoc());
298                     Policy.getLog().log(status);
299
300                     // And remember this item so we don't log it again.
301
loggedCommandIds.add(commandId);
302                     command.addCommandListener(new ICommandListener() {
303                         /*
304                          * (non-Javadoc)
305                          *
306                          * @see org.eclipse.ui.commands.ICommandListener#commandChanged(org.eclipse.ui.commands.CommandEvent)
307                          */

308                         public final void commandChanged(
309                                 final CommandEvent commandEvent) {
310                             if (command.isDefined()) {
311                                 command.removeCommandListener(this);
312                                 loggedCommandIds.remove(commandId);
313                             }
314                         }
315                     });
316
317                     return true;
318                 }
319
320                 return activeChecker.isActive(commandId);
321             }
322
323             return true;
324         }
325
326         /**
327          * @see org.eclipse.jface.action.ExternalActionManager.ICallback#removePropertyChangeListener(String,
328          * IPropertyChangeListener)
329          */

330         public final void removePropertyChangeListener(final String JavaDoc commandId,
331                 final IPropertyChangeListener listener) {
332             final IPropertyChangeListener existingListener = (IPropertyChangeListener) registeredListeners
333                     .get(commandId);
334             if (existingListener == listener) {
335                 registeredListeners.remove(commandId);
336                 if (registeredListeners.isEmpty()) {
337                     bindingManager.removeBindingManagerListener(this);
338                     bindingManagerListenerAttached = false;
339                 }
340             }
341         }
342     }
343
344     /**
345      * Defines a callback mechanism for developer who wish to further control
346      * the visibility of legacy action-based contribution items.
347      *
348      * @since 3.1
349      */

350     public static interface IActiveChecker {
351         /**
352          * Checks whether the command with the given identifier should be
353          * considered active. This can be used in systems using some kind of
354          * user interface filtering (e.g., activities in the Eclipse workbench).
355          *
356          * @param commandId
357          * The identifier for the command; must not be
358          * <code>null</code>
359          * @return <code>true</code> if the command is active;
360          * <code>false</code> otherwise.
361          */

362         public boolean isActive(String JavaDoc commandId);
363     }
364
365     /**
366      * <p>
367      * A callback which communicates with the applications binding manager. This
368      * interface provides more information from the binding manager, which
369      * allows greater integration. Implementing this interface is preferred over
370      * {@link ExternalActionManager.ICallback}.
371      * </p>
372      * <p>
373      * Clients may implement this interface, but must not extend.
374      * </p>
375      *
376      * @since 3.2
377      */

378     public static interface IBindingManagerCallback extends ICallback {
379
380         /**
381          * <p>
382          * Returns the active bindings for a particular command identifier.
383          * </p>
384          *
385          * @param commandId
386          * The identifier of the command whose bindings are
387          * requested. This argument may be <code>null</code>. It
388          * is assumed that the command has no parameters.
389          * @return The array of active triggers (<code>TriggerSequence</code>)
390          * for a particular command identifier. This value is guaranteed
391          * not to be <code>null</code>, but it may be empty.
392          */

393         public TriggerSequence[] getActiveBindingsFor(String JavaDoc commandId);
394     }
395
396     /**
397      * A callback mechanism for some external tool to communicate extra
398      * information to actions and action contribution items.
399      *
400      * @since 3.0
401      */

402     public static interface ICallback {
403
404         /**
405          * <p>
406          * Adds a listener to the object referenced by <code>identifier</code>.
407          * This listener will be notified if a property of the item is to be
408          * changed. This identifier is specific to mechanism being used. In the
409          * case of the Eclipse workbench, this is the command identifier.
410          * </p>
411          * <p>
412          * A single instance of the listener may only ever be associated with
413          * one identifier. Attempts to add the listener twice (without a removal
414          * in between) has undefined behaviour.
415          * </p>
416          *
417          * @param identifier
418          * The identifier of the item to which the listener should be
419          * attached; must not be <code>null</code>.
420          * @param listener
421          * The listener to be added; must not be <code>null</code>.
422          */

423         public void addPropertyChangeListener(String JavaDoc identifier,
424                 IPropertyChangeListener listener);
425
426         /**
427          * An accessor for the accelerator associated with the item indicated by
428          * the identifier. This identifier is specific to mechanism being used.
429          * In the case of the Eclipse workbench, this is the command identifier.
430          *
431          * @param identifier
432          * The identifier of the item from which the accelerator
433          * should be obtained ; must not be <code>null</code>.
434          * @return An integer representation of the accelerator. This is the
435          * same accelerator format used by SWT.
436          */

437         public Integer JavaDoc getAccelerator(String JavaDoc identifier);
438
439         /**
440          * An accessor for the accelerator text associated with the item
441          * indicated by the identifier. This identifier is specific to mechanism
442          * being used. In the case of the Eclipse workbench, this is the command
443          * identifier.
444          *
445          * @param identifier
446          * The identifier of the item from which the accelerator text
447          * should be obtained ; must not be <code>null</code>.
448          * @return A string representation of the accelerator. This is the
449          * string representation that should be displayed to the user.
450          */

451         public String JavaDoc getAcceleratorText(String JavaDoc identifier);
452
453         /**
454          * Checks to see whether the given accelerator is being used by some
455          * other mechanism (outside of the menus controlled by JFace). This is
456          * used to keep JFace from trying to grab accelerators away from someone
457          * else.
458          *
459          * @param accelerator
460          * The accelerator to check -- in SWT's internal accelerator
461          * format.
462          * @return <code>true</code> if the accelerator is already being used
463          * and shouldn't be used again; <code>false</code> otherwise.
464          */

465         public boolean isAcceleratorInUse(int accelerator);
466
467         /**
468          * Checks whether the item matching this identifier is active. This is
469          * used to decide whether a contribution item with this identifier
470          * should be made visible. An inactive item is not visible.
471          *
472          * @param identifier
473          * The identifier of the item from which the active state
474          * should be retrieved; must not be <code>null</code>.
475          * @return <code>true</code> if the item is active; <code>false</code>
476          * otherwise.
477          */

478         public boolean isActive(String JavaDoc identifier);
479
480         /**
481          * Removes a listener from the object referenced by
482          * <code>identifier</code>. This identifier is specific to mechanism
483          * being used. In the case of the Eclipse workbench, this is the command
484          * identifier.
485          *
486          * @param identifier
487          * The identifier of the item to from the listener should be
488          * removed; must not be <code>null</code>.
489          * @param listener
490          * The listener to be removed; must not be <code>null</code>.
491          */

492         public void removePropertyChangeListener(String JavaDoc identifier,
493                 IPropertyChangeListener listener);
494     }
495
496     /**
497      * The singleton instance of this class. This value may be <code>null</code>--
498      * if it has not yet been initialized.
499      */

500     private static ExternalActionManager instance;
501
502     /**
503      * Retrieves the current singleton instance of this class.
504      *
505      * @return The singleton instance; this value is never <code>null</code>.
506      */

507     public static ExternalActionManager getInstance() {
508         if (instance == null) {
509             instance = new ExternalActionManager();
510         }
511
512         return instance;
513     }
514
515     /**
516      * The callback mechanism to use to retrieve extra information.
517      */

518     private ICallback callback;
519
520     /**
521      * Constructs a new instance of <code>ExternalActionManager</code>.
522      */

523     private ExternalActionManager() {
524         // This is a singleton class. Only this class should create an instance.
525
}
526
527     /**
528      * An accessor for the current call back.
529      *
530      * @return The current callback mechanism being used. This is the callback
531      * that should be queried for extra information about actions and
532      * action contribution items. This value may be <code>null</code>
533      * if there is no extra information.
534      */

535     public ICallback getCallback() {
536         return callback;
537     }
538
539     /**
540      * A mutator for the current call back
541      *
542      * @param callbackToUse
543      * The new callback mechanism to use; this value may be
544      * <code>null</code> if the default is acceptable (i.e., no
545      * extra information will provided to actions).
546      */

547     public void setCallback(ICallback callbackToUse) {
548         callback = callbackToUse;
549     }
550 }
551
Popular Tags