001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.command;
017
018 import java.awt.Color;
019 import java.awt.Container;
020 import java.beans.PropertyChangeEvent;
021 import java.beans.PropertyChangeListener;
022 import java.util.Collections;
023 import java.util.Iterator;
024 import java.util.Map;
025 import java.util.NoSuchElementException;
026
027 import javax.swing.AbstractButton;
028 import javax.swing.Icon;
029 import javax.swing.JComponent;
030 import javax.swing.JMenuItem;
031 import javax.swing.KeyStroke;
032 import javax.swing.SwingUtilities;
033
034 import org.springframework.beans.factory.BeanNameAware;
035 import org.springframework.beans.factory.InitializingBean;
036 import org.springframework.binding.value.support.AbstractPropertyChangePublisher;
037 import org.springframework.core.style.ToStringCreator;
038 import org.springframework.richclient.application.ApplicationServicesLocator;
039 import org.springframework.richclient.command.config.CommandButtonConfigurer;
040 import org.springframework.richclient.command.config.CommandButtonIconInfo;
041 import org.springframework.richclient.command.config.CommandButtonLabelInfo;
042 import org.springframework.richclient.command.config.CommandFaceDescriptor;
043 import org.springframework.richclient.command.config.CommandFaceDescriptorRegistry;
044 import org.springframework.richclient.command.support.CommandFaceButtonManager;
045 import org.springframework.richclient.core.SecurityControllable;
046 import org.springframework.richclient.factory.ButtonFactory;
047 import org.springframework.richclient.factory.ComponentFactory;
048 import org.springframework.richclient.factory.MenuFactory;
049 import org.springframework.util.Assert;
050 import org.springframework.util.CachingMapDecorator;
051 import org.springframework.util.StringUtils;
052
053 /**
054 * <p>
055 * Base class for commands. Extend this class by implementing the
056 * {@link #execute()} method.
057 * </p>
058 *
059 * <p>
060 * Most (if not all) commands result in a UI component. Several methods are
061 * provided here to deliver abstractButtons or menuItems. Configuring this
062 * visual aspect of the command is done by a number of
063 * {@link CommandFaceDescriptor}s. One of these will be registered as the
064 * default while others can be used to create a different look by providing a
065 * faceDescriptorId.
066 * </p>
067 *
068 * @see CommandFaceDescriptor
069 *
070 * @author Keith Donald
071 * @author Jan Hoskens
072 *
073 */
074 public abstract class AbstractCommand extends AbstractPropertyChangePublisher implements InitializingBean,
075 BeanNameAware, GuardedActionCommandExecutor, SecurityControllable {
076
077 /** Property used to notify changes in the <em>enabled</em> state. */
078 public static final String ENABLED_PROPERTY_NAME = "enabled";
079
080 /** Property used to notify changes in the <em>visible</em> state. */
081 public static final String VISIBLE_PROPERTY_NAME = "visible";
082
083 private static final String DEFAULT_FACE_DESCRIPTOR_ID = "default";
084
085 private String id;
086
087 private String defaultFaceDescriptorId = DEFAULT_FACE_DESCRIPTOR_ID;
088
089 private boolean enabled = true;
090
091 private boolean visible = true;
092
093 private boolean authorized = true;
094
095 private String securityControllerId = null;
096
097 private Map faceButtonManagers;
098
099 private CommandServices commandServices;
100
101 private CommandFaceDescriptorRegistry faceDescriptorRegistry;
102
103 private Boolean oldEnabledState;
104
105 private Boolean oldVisibleState;
106
107 /**
108 * Default constructor. Id can be set by context.
109 *
110 * @see BeanNameAware
111 */
112 protected AbstractCommand() {
113 this(null);
114 }
115
116 /**
117 * Constructor providing an id for configuration.
118 *
119 * @param id
120 */
121 protected AbstractCommand(String id) {
122 super();
123 setId(id);
124 // keep track of enable state for buttons
125 addEnabledListener(new ButtonEnablingListener());
126 // keep track of visible state for buttons
127 addPropertyChangeListener(VISIBLE_PROPERTY_NAME, new ButtonVisibleListener());
128 }
129
130 /**
131 * Constructor providing id and encodedLabel. A default FaceDescriptor will
132 * be created by passing the encodedLabel.
133 *
134 * @param id
135 * @param encodedLabel label to use when creating the default
136 * {@link CommandFaceDescriptor}.
137 */
138 protected AbstractCommand(String id, String encodedLabel) {
139 this(id, new CommandFaceDescriptor(encodedLabel));
140 }
141
142 /**
143 * Constructor providing id and a number of parameters to create a default
144 * {@link CommandFaceDescriptor}.
145 *
146 * @param id
147 * @param encodedLabel label for the default {@link CommandFaceDescriptor}.
148 * @param icon icon for the default {@link CommandFaceDescriptor}.
149 * @param caption caption for the default {@link CommandFaceDescriptor}.
150 */
151 protected AbstractCommand(String id, String encodedLabel, Icon icon, String caption) {
152 this(id, new CommandFaceDescriptor(encodedLabel, icon, caption));
153 }
154
155 /**
156 * Constructor providing an id and the default FaceDescriptor.
157 *
158 * @param id
159 * @param faceDescriptor the default FaceDescriptor to use.
160 */
161 protected AbstractCommand(String id, CommandFaceDescriptor faceDescriptor) {
162 this(id);
163 if (faceDescriptor != null) {
164 setFaceDescriptor(faceDescriptor);
165 }
166 }
167
168 /**
169 * Constructor providing an id and a number of FaceDescriptors. No default
170 * faceDescriptor is set.
171 *
172 * @param id
173 * @param faceDescriptors a map which contains <faceDescriptorId,
174 * faceDescriptor> pairs.
175 */
176 protected AbstractCommand(String id, Map faceDescriptors) {
177 this(id);
178 setFaceDescriptors(faceDescriptors);
179 }
180
181 /**
182 * @return id of this Command.
183 */
184 public String getId() {
185 return this.id;
186 }
187
188 /**
189 * Set the id. In most cases, this is provided by the constructor or through
190 * the beanId provided in the applicationContext.
191 *
192 * @param id
193 */
194 protected void setId(String id) {
195 if (!StringUtils.hasText(id)) {
196 id = null;
197 }
198 this.id = id;
199 }
200
201 /**
202 * {@inheritDoc}
203 */
204 public void setBeanName(String name) {
205 if (getId() == null) {
206 setId(name);
207 }
208 }
209
210 /**
211 * Set the default faceDescriptor to use for this command.
212 *
213 * @param faceDescriptor the {@link CommandFaceDescriptor} to use as
214 * default.
215 */
216 public void setFaceDescriptor(CommandFaceDescriptor faceDescriptor) {
217 setFaceDescriptor(getDefaultFaceDescriptorId(), faceDescriptor);
218 }
219
220 /**
221 * Add an additional {@link CommandFaceDescriptor}.
222 *
223 * @param faceDescriptorId key to identify and use this faceDescriptor.
224 * @param faceDescriptor additional {@link CommandFaceDescriptor}.
225 */
226 public void setFaceDescriptor(String faceDescriptorId, CommandFaceDescriptor faceDescriptor) {
227 getButtonManager(faceDescriptorId).setFaceDescriptor(faceDescriptor);
228 }
229
230 /**
231 * Add a number of {@link CommandFaceDescriptor}s to this Command.
232 *
233 * @param faceDescriptors a {@link Map} which contains <faceDescriptorId,
234 * CommandFaceDescriptor> pairs.
235 */
236 public void setFaceDescriptors(Map faceDescriptors) {
237 Assert.notNull(faceDescriptors);
238 Iterator it = faceDescriptors.entrySet().iterator();
239 while (it.hasNext()) {
240 Map.Entry entry = (Map.Entry) it.next();
241 String faceDescriptorId = (String) entry.getKey();
242 CommandFaceDescriptor faceDescriptor = (CommandFaceDescriptor) entry.getValue();
243 setFaceDescriptor(faceDescriptorId, faceDescriptor);
244 }
245 }
246
247 /**
248 * Change the default FaceDescriptor.
249 *
250 * @param defaultFaceDescriptorId the id of the faceDescriptor to be used as
251 * default.
252 */
253 public void setDefaultFaceDescriptorId(String defaultFaceDescriptorId) {
254 this.defaultFaceDescriptorId = defaultFaceDescriptorId;
255 }
256
257 /**
258 * Set the {@link CommandFaceDescriptorRegistry} to use when
259 * registering/looking up {@link CommandFaceDescriptor}s.
260 *
261 * @param faceDescriptorRegistry registry to use for the
262 * {@link CommandFaceDescriptor}s.
263 */
264 public void setFaceDescriptorRegistry(CommandFaceDescriptorRegistry faceDescriptorRegistry) {
265 this.faceDescriptorRegistry = faceDescriptorRegistry;
266 }
267
268 /**
269 * Set the {@link CommandServices}.
270 */
271 public void setCommandServices(CommandServices services) {
272 this.commandServices = services;
273 }
274
275 /**
276 * Set the provided label on the default {@link CommandFaceDescriptor}.
277 *
278 * @see CommandFaceDescriptor#setButtonLabelInfo(String)
279 */
280 public void setLabel(String encodedLabel) {
281 getOrCreateFaceDescriptor().setButtonLabelInfo(encodedLabel);
282 }
283
284 /**
285 * Set the provided label on the default {@link CommandFaceDescriptor}.
286 *
287 * @see CommandFaceDescriptor#setLabelInfo(String)
288 */
289 public void setLabel(CommandButtonLabelInfo label) {
290 getOrCreateFaceDescriptor().setLabelInfo(label);
291 }
292
293 /**
294 * Set the provided description on the default {@link CommandFaceDescriptor}.
295 *
296 * @see CommandFaceDescriptor#setCaption(String)
297 */
298 public void setCaption(String shortDescription) {
299 getOrCreateFaceDescriptor().setCaption(shortDescription);
300 }
301
302 /**
303 * Set the provided icon on the default {@link CommandFaceDescriptor}.
304 *
305 * @see CommandFaceDescriptor#setIcon(Icon)
306 */
307 public void setIcon(Icon icon) {
308 getOrCreateFaceDescriptor().setIcon(icon);
309 }
310
311 /**
312 * Set the provided iconInfo on the default {@link CommandFaceDescriptor}.
313 *
314 * @see CommandFaceDescriptor#setIconInfo(CommandButtonIconInfo)
315 */
316 public void setIconInfo(CommandButtonIconInfo iconInfo) {
317 getOrCreateFaceDescriptor().setIconInfo(iconInfo);
318 }
319
320 /**
321 * Set the provided foreground colour on the default {@link CommandFaceDescriptor}.
322 *
323 * @see CommandFaceDescriptor#setForeground(Color)
324 */
325 public void setForeground(Color foreground) {
326 getOrCreateFaceDescriptor().setForeground(foreground);
327 }
328
329 /**
330 * Set the provided background colour on the default {@link CommandFaceDescriptor}.
331 *
332 * @see CommandFaceDescriptor#setBackground(Color)
333 */
334 public void setBackground(Color background) {
335 getOrCreateFaceDescriptor().setBackground(background);
336 }
337
338 /**
339 * Performs initialisation and validation of this instance after its
340 * dependencies have been set. If subclasses override this method, they
341 * should begin by calling {@code super.afterPropertiesSet()}.
342 */
343 public void afterPropertiesSet() {
344 if (getId() == null) {
345 logger.info("Command " + this + " has no set id; note: anonymous commands cannot be used in registries.");
346 }
347 if (this instanceof ActionCommand && !isFaceConfigured()) {
348 logger.warn("The face descriptor property is not yet set for action command '" + getId()
349 + "'; command won't render correctly until this is configured");
350 }
351 }
352
353 /**
354 * Returns the defaultFaceDescriptor. Creates one if needed.
355 */
356 private CommandFaceDescriptor getOrCreateFaceDescriptor() {
357 if (!isFaceConfigured()) {
358 if (logger.isInfoEnabled()) {
359 logger.info("Lazily instantiating default face descriptor on behalf of caller to prevent npe; "
360 + "command is being configured manually, right?");
361 }
362 setFaceDescriptor(new CommandFaceDescriptor());
363 }
364 return getFaceDescriptor();
365 }
366
367 /**
368 * Returns the default faceDescriptorId.
369 */
370 public String getDefaultFaceDescriptorId() {
371 if (!StringUtils.hasText(defaultFaceDescriptorId)) {
372 return DEFAULT_FACE_DESCRIPTOR_ID;
373 }
374 return defaultFaceDescriptorId;
375 }
376
377 /**
378 * Returns the default faceDescriptor.
379 */
380 protected CommandFaceDescriptor getFaceDescriptor() {
381 return getDefaultButtonManager().getFaceDescriptor();
382 }
383
384 /**
385 * Returns <code>true</code> if this command has a default faceDescriptor.
386 */
387 public boolean isFaceConfigured() {
388 return getDefaultButtonManager().isFaceConfigured();
389 }
390
391 /**
392 * Returns the icon from the default faceDescriptor or <code>null</code>
393 * if no faceDescriptor is available.
394 */
395 public Icon getIcon() {
396 if (isFaceConfigured()) {
397 return getFaceDescriptor().getIcon();
398 }
399 return null;
400 }
401
402 /**
403 * Returns the text from the default faceDescriptor or the default text of
404 * the {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getText()}.
405 */
406 public String getText() {
407 if (isFaceConfigured()) {
408 return getFaceDescriptor().getText();
409 }
410 return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getText();
411 }
412
413 /**
414 * Returns the mnemonic from the default faceDescriptor or the default
415 * mnemonic of the
416 * {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getMnemonic()}.
417 */
418 public int getMnemonic() {
419 if (isFaceConfigured()) {
420 return getFaceDescriptor().getMnemonic();
421 }
422 return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getMnemonic();
423 }
424
425 /**
426 * Returns the mnemonicIndex from the default faceDescriptor or the default
427 * mnemonicIndex of the
428 * {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getMnemonicIndex()}.
429 */
430 public int getMnemonicIndex() {
431 if (isFaceConfigured()) {
432 return getFaceDescriptor().getMnemonicIndex();
433 }
434 return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getMnemonicIndex();
435 }
436
437 /**
438 * Returns the accelerator from the default faceDescriptor or the default
439 * accelerator of the
440 * {@link CommandButtonLabelInfo#BLANK_BUTTON_LABEL#getAccelerator()}.
441 */
442 public KeyStroke getAccelerator() {
443 if (isFaceConfigured()) {
444 return getFaceDescriptor().getAccelerator();
445 }
446 return CommandButtonLabelInfo.BLANK_BUTTON_LABEL.getAccelerator();
447 }
448
449 /**
450 * Returns the {@link CommandFaceDescriptorRegistry} of this
451 * {@link AbstractCommand} which holds all face descriptors.
452 */
453 public CommandFaceDescriptorRegistry getFaceDescriptorRegistry() {
454 return faceDescriptorRegistry;
455 }
456
457 /**
458 * Returns the {@link CommandServices} for this {@link AbstractCommand}.
459 */
460 protected CommandServices getCommandServices() {
461 if (commandServices == null) {
462 commandServices = (CommandServices) ApplicationServicesLocator.services().getService(CommandServices.class);
463 }
464 return this.commandServices;
465 }
466
467 /**
468 * Set the Id of the security controller that should manage this object.
469 * @param controllerId Id (bean name) of the security controller
470 */
471 public void setSecurityControllerId(String controllerId) {
472 this.securityControllerId = controllerId;
473 }
474
475 /**
476 * Get the id (bean name) of the security controller that should manage this
477 * object.
478 * @return controller id
479 */
480 public String getSecurityControllerId() {
481 return securityControllerId;
482 }
483
484 /**
485 * Set the authorized state. Setting authorized to false will override any
486 * call to {@link #setEnabled(boolean)}. As long as this object is
487 * unauthorized, it can not be enabled.
488 * @param authorized Pass <code>true</code> if the object is to be
489 * authorized
490 */
491 public void setAuthorized(boolean authorized) {
492 boolean wasAuthorized = isAuthorized();
493 if (hasChanged(wasAuthorized, authorized)) {
494 this.authorized = authorized;
495 firePropertyChange(AUTHORIZED_PROPERTY, wasAuthorized, authorized);
496 updatedEnabledState();
497 }
498 }
499
500 /**
501 * Returns <code>true</code> if the command is authorized.
502 */
503 public boolean isAuthorized() {
504 return authorized;
505 }
506
507 /**
508 * Returns <code>true</code> if the command is enabled and
509 * {@link #isAuthorized()}.
510 *
511 * @see #isAuthorized()
512 */
513 public boolean isEnabled() {
514 return enabled && isAuthorized();
515 }
516
517 /**
518 * This method is called when any predicate for enabled state has changed.
519 * This implementation fires the enabled changed event if the return value
520 * of {@link #isEnabled()} has changed.
521 * <p>
522 * Subclasses which have an additional predicate to enabled state must call
523 * this method if the state of the predicate changes.
524 */
525 protected void updatedEnabledState() {
526 boolean isEnabled = isEnabled();
527 if (oldEnabledState == null || hasChanged(oldEnabledState.booleanValue(), isEnabled)) {
528 firePropertyChange(ENABLED_PROPERTY_NAME, oldEnabledState == null ? !isEnabled : oldEnabledState
529 .booleanValue(), isEnabled);
530 }
531 oldEnabledState = Boolean.valueOf(isEnabled);
532 }
533
534 /**
535 * Set the enabled state of this command. Note that if we are currently not
536 * authorized, then the new value will just be recorded and no change in the
537 * current enabled state will be made.
538 * @param enabled state
539 */
540 public void setEnabled(boolean enabled) {
541 if (hasChanged(this.enabled, enabled)) {
542 this.enabled = enabled;
543 updatedEnabledState();
544 }
545 }
546
547 /**
548 * Listener to keep track of enabled state. When enable on command changes,
549 * each button has to be checked and set.
550 */
551 private class ButtonEnablingListener implements PropertyChangeListener {
552 public void propertyChange(PropertyChangeEvent evt) {
553 // We need to keep the buttons in sync with the command, so go
554 // through the buttons and set Enabled state.
555 // alternative is to add a listener to the enabled value and change
556 // buttons in that listener
557 // NOT redundant
558 boolean enabled = evt.getNewValue() == Boolean.TRUE;
559 Iterator it = buttonIterator();
560 while (it.hasNext()) {
561 AbstractButton button = (AbstractButton) it.next();
562 button.setEnabled(enabled);
563 }
564 }
565 }
566
567 /**
568 * Listener to keep track of visible state. When visible on command changes,
569 * each button has to be checked and set.
570 */
571 private class ButtonVisibleListener implements PropertyChangeListener {
572 public void propertyChange(PropertyChangeEvent evt) {
573 // We need to keep the buttons in sync with the command, so go
574 // through the buttons and set visible state.
575 // alternative is to add a listener to the visible value and change
576 // buttons in that listener
577 // NOT redundant
578 boolean enabled = evt.getNewValue() == Boolean.TRUE;
579 Iterator it = buttonIterator();
580 while (it.hasNext()) {
581 AbstractButton button = (AbstractButton) it.next();
582 button.setVisible(enabled);
583 }
584 }
585 }
586
587 /**
588 * {@inheritDoc}
589 */
590 public void addEnabledListener(PropertyChangeListener listener) {
591 addPropertyChangeListener(ENABLED_PROPERTY_NAME, listener);
592 }
593
594 /**
595 * {@inheritDoc}
596 */
597 public void removeEnabledListener(PropertyChangeListener listener) {
598 removePropertyChangeListener(ENABLED_PROPERTY_NAME, listener);
599 }
600
601 /**
602 * <p>
603 * Returns an iterator over all buttons in the default
604 * {@link CommandFaceButtonManager}.
605 * </p>
606 * <p>
607 * To traverse all buttons of all {@link CommandFaceButtonManager}s see
608 * {@link #buttonIterator()}.
609 * </p>
610 */
611 protected final Iterator defaultButtonIterator() {
612 return getDefaultButtonManager().iterator();
613 }
614
615 /**
616 * Returns an iterator over <em>all</em> buttons by traversing
617 * <em>each</em> {@link CommandFaceButtonManager}.
618 */
619 protected final Iterator buttonIterator() {
620
621 if (this.faceButtonManagers == null)
622 return Collections.EMPTY_SET.iterator();
623
624 return new NestedButtonIterator(this.faceButtonManagers.values().iterator());
625 }
626
627 /**
628 * Iterator to traverse all buttons in every
629 * {@link CommandFaceButtonManager} of this {@link AbstractCommand}.
630 */
631 private static final class NestedButtonIterator implements Iterator {
632 private final Iterator managerIterator;
633
634 private Iterator currentButtonIterator;
635
636 private AbstractButton nextButton;
637
638 NestedButtonIterator(Iterator it) {
639 this.managerIterator = it;
640 preFetchNextButton();
641 }
642
643 public boolean hasNext() {
644 return nextButton != null;
645 }
646
647 public Object next() {
648 if (nextButton == null) {
649 throw new NoSuchElementException();
650 }
651 AbstractButton lastButton = nextButton;
652 preFetchNextButton();
653 return lastButton;
654 }
655
656 public void remove() {
657 throw new UnsupportedOperationException("Can't use a button-iterator on AbstractCommand to remove buttons.");
658 }
659
660 private void preFetchNextButton() {
661 while (this.currentButtonIterator == null || !this.currentButtonIterator.hasNext()) {
662 if (this.managerIterator.hasNext()) {
663 CommandFaceButtonManager cfbm = (CommandFaceButtonManager) this.managerIterator.next();
664 this.currentButtonIterator = cfbm.iterator();
665 }
666 else {
667 this.currentButtonIterator = null;
668 this.nextButton = null;
669 return;
670 }
671 }
672
673 if (this.currentButtonIterator.hasNext())
674 nextButton = (AbstractButton) this.currentButtonIterator.next();
675 else
676 nextButton = null;
677 }
678 }
679
680 /**
681 * Returns <code>true</code> if this command doesn't have an Id.
682 */
683 public boolean isAnonymous() {
684 return id == null;
685 }
686
687 /**
688 * Returns <code>true</code> if the command is visible.
689 */
690 public boolean isVisible() {
691 return this.visible;
692 }
693
694 /**
695 * Set this command visible and update all associated buttons.
696 */
697 public void setVisible(boolean value) {
698 if (visible != value) {
699 this.visible = value;
700 updatedVisibleState();
701 }
702 }
703
704 /**
705 * <p>
706 * This method is called when any predicate for visible state has changed.
707 * This implementation fires the visible changed event if the return value
708 * of {@link #isVisible()} has changed.
709 * </p>
710 * <p>
711 * Subclasses which have an additional predicate to visible state must call
712 * this method if the state of the predicate changes.
713 * </p>
714 */
715 protected void updatedVisibleState() {
716 boolean isVisible = isVisible();
717 if (oldVisibleState == null || hasChanged(oldVisibleState.booleanValue(), isVisible)) {
718 firePropertyChange(VISIBLE_PROPERTY_NAME, oldVisibleState == null ? !isVisible : oldVisibleState
719 .booleanValue(), isVisible);
720 }
721 oldVisibleState = Boolean.valueOf(isVisible);
722 }
723
724 /**
725 * Create a button using the defaults for faceDescriptorId, buttonFactory
726 * and buttonConfigurer.
727 *
728 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
729 */
730 public final AbstractButton createButton() {
731 return createButton(getDefaultFaceDescriptorId(), getButtonFactory(), getDefaultButtonConfigurer());
732 }
733
734 /**
735 * Create a button using the defaults for buttonFactory and
736 * buttonConfigurer.
737 *
738 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
739 */
740 public final AbstractButton createButton(String faceDescriptorId) {
741 return createButton(faceDescriptorId, getButtonFactory(), getDefaultButtonConfigurer());
742 }
743
744 /**
745 * Create a button using the defaults for faceDescriptorId and
746 * buttonConfigurer.
747 *
748 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
749 */
750 public final AbstractButton createButton(ButtonFactory buttonFactory) {
751 return createButton(getDefaultFaceDescriptorId(), buttonFactory, getDefaultButtonConfigurer());
752 }
753
754 /**
755 * Create a button using the default buttonConfigurer.
756 *
757 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
758 */
759 public final AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory) {
760 return createButton(faceDescriptorId, buttonFactory, getDefaultButtonConfigurer());
761 }
762
763 /**
764 * Create a button using the default buttonFactory.
765 *
766 * @see #createButton(String, ButtonFactory, CommandButtonConfigurer)
767 */
768 public final AbstractButton createButton(ButtonFactory buttonFactory, CommandButtonConfigurer buttonConfigurer) {
769 return createButton(getDefaultFaceDescriptorId(), buttonFactory, buttonConfigurer);
770 }
771
772 /**
773 * Creates a button using the provided id, factory and configurer.
774 *
775 * @param faceDescriptorId id of the faceDescriptor used to configure the
776 * button.
777 * @param buttonFactory factory that delivers the button.
778 * @param buttonConfigurer configurer mapping the faceDescriptor on the
779 * button.
780 * @return a button attached to this command.
781 */
782 public AbstractButton createButton(String faceDescriptorId, ButtonFactory buttonFactory,
783 CommandButtonConfigurer buttonConfigurer) {
784 AbstractButton button = buttonFactory.createButton();
785 attach(button, faceDescriptorId, buttonConfigurer);
786 return button;
787 }
788
789 /**
790 * Create a menuItem using the defaults for faceDescriptorId, menuFactory
791 * and menuItemButtonConfigurer.
792 *
793 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
794 */
795 public final JMenuItem createMenuItem() {
796 return createMenuItem(getDefaultFaceDescriptorId(), getMenuFactory(), getMenuItemButtonConfigurer());
797 }
798
799 /**
800 * Create a menuItem using the defaults for menuFactory and
801 * menuItemButtonConfigurer.
802 *
803 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
804 */
805 public final JMenuItem createMenuItem(String faceDescriptorId) {
806 return createMenuItem(faceDescriptorId, getMenuFactory(), getMenuItemButtonConfigurer());
807 }
808
809 /**
810 * Create a menuItem using the defaults for faceDescriptorId and
811 * menuItemButtonConfigurer.
812 *
813 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
814 */
815 public final JMenuItem createMenuItem(MenuFactory menuFactory) {
816 return createMenuItem(getDefaultFaceDescriptorId(), menuFactory, getMenuItemButtonConfigurer());
817 }
818
819 /**
820 * Create a menuItem using the default and menuItemButtonConfigurer.
821 *
822 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
823 */
824 public final JMenuItem createMenuItem(String faceDescriptorId, MenuFactory menuFactory) {
825 return createMenuItem(faceDescriptorId, menuFactory, getMenuItemButtonConfigurer());
826 }
827
828 /**
829 * Create a menuItem using the default faceDescriptorId.
830 *
831 * @see #createMenuItem(String, MenuFactory, CommandButtonConfigurer)
832 */
833 public final JMenuItem createMenuItem(MenuFactory menuFactory, CommandButtonConfigurer buttonConfigurer) {
834 return createMenuItem(getDefaultFaceDescriptorId(), menuFactory, buttonConfigurer);
835 }
836
837 /**
838 * Create a menuItem using the provided id, factory and configurer.
839 *
840 * @param faceDescriptorId id of the faceDescriptor used to configure the
841 * button.
842 * @param menuFactory factory that delivers the menuItem.
843 * @param buttonConfigurer configurer mapping the faceDescriptor on the
844 * button.
845 * @return a menuItem attached to this command.
846 */
847 public JMenuItem createMenuItem(String faceDescriptorId, MenuFactory menuFactory,
848 CommandButtonConfigurer buttonConfigurer) {
849 JMenuItem menuItem = menuFactory.createMenuItem();
850 attach(menuItem, faceDescriptorId, buttonConfigurer);
851 return menuItem;
852 }
853
854 /**
855 * Attach and configure the button to the default faceDescriptor using the
856 * default configurer.
857 *
858 * @see #attach(AbstractButton, String, CommandButtonConfigurer)
859 */
860 public void attach(AbstractButton button) {
861 attach(button, getDefaultFaceDescriptorId(), getCommandServices().getDefaultButtonConfigurer());
862 }
863
864 /**
865 * Attach and configure the button to the default faceDescriptor using the
866 * given configurer.
867 *
868 * @see #attach(AbstractButton, String, CommandButtonConfigurer)
869 */
870 public void attach(AbstractButton button, CommandButtonConfigurer configurer) {
871 attach(button, getDefaultFaceDescriptorId(), configurer);
872 }
873
874 /**
875 * Attach and configure the button to the faceDescriptorId using the configurer.
876 *
877 * @param button the button to attach and configure.
878 * @param faceDescriptorId the id of the faceDescriptor.
879 * @param configurer that maps the faceDescriptor on the button.
880 */
881 public void attach(AbstractButton button, String faceDescriptorId, CommandButtonConfigurer configurer) {
882 getButtonManager(faceDescriptorId).attachAndConfigure(button, configurer);
883 onButtonAttached(button);
884 }
885
886 /**
887 * Additional code to execute when attaching a button.
888 *
889 * @param button the button that has been attached.
890 */
891 protected void onButtonAttached(AbstractButton button) {
892 if (logger.isDebugEnabled()) {
893 logger.debug("Configuring newly attached button for command '" + getId() + "' enabled=" + isEnabled()
894 + ", visible=" + isVisible());
895 }
896 button.setEnabled(isEnabled());
897 button.setVisible(isVisible());
898 }
899
900 /**
901 * Detach the button from the {@link CommandFaceButtonManager}.
902 *
903 * @param button the button to detach.
904 */
905 public void detach(AbstractButton button) {
906 if (getDefaultButtonManager().isAttachedTo(button)) {
907 getDefaultButtonManager().detach(button);
908 onButtonDetached();
909 }
910 }
911
912 /**
913 * Returns <code>true</code> if the provided button is attached to the
914 * default {@link CommandFaceButtonManager}.
915 *
916 * @param b the button to check.
917 * @return <code>true</code> if b is attached to the default
918 * {@link CommandFaceButtonManager}.
919 */
920 public boolean isAttached(AbstractButton b) {
921 return getDefaultButtonManager().isAttachedTo(b);
922 }
923
924 /**
925 * Implement this to add custom code executed when detaching a button.
926 */
927 protected void onButtonDetached() {
928 // default no implementation, subclasses may override
929 }
930
931 /**
932 * Returns the {@link CommandFaceButtonManager} for the default
933 * {@link CommandFaceDescriptor}.
934 */
935 private CommandFaceButtonManager getDefaultButtonManager() {
936 return getButtonManager(getDefaultFaceDescriptorId());
937 }
938
939 /**
940 * Returns the {@link CommandFaceButtonManager} for the given
941 * faceDescriptorId.
942 *
943 * @param faceDescriptorId id of the {@link CommandFaceDescriptor}.
944 * @return the {@link CommandFaceButtonManager} managing buttons configured
945 * with the {@link CommandFaceDescriptor}.
946 */
947 private CommandFaceButtonManager getButtonManager(String faceDescriptorId) {
948 if (this.faceButtonManagers == null) {
949 this.faceButtonManagers = new CachingMapDecorator() {
950 protected Object create(Object key) {
951 return new CommandFaceButtonManager(AbstractCommand.this, (String) key);
952 }
953 };
954 }
955 CommandFaceButtonManager m = (CommandFaceButtonManager) this.faceButtonManagers.get(faceDescriptorId);
956 return m;
957 }
958
959 /**
960 * @see CommandServices#getDefaultButtonConfigurer()
961 */
962 protected CommandButtonConfigurer getDefaultButtonConfigurer() {
963 return getCommandServices().getDefaultButtonConfigurer();
964 }
965
966 /**
967 * @see CommandServices#getToolBarButtonConfigurer()
968 */
969 protected CommandButtonConfigurer getToolBarButtonConfigurer() {
970 return getCommandServices().getToolBarButtonConfigurer();
971 }
972
973 /**
974 * @see CommandServices#getToolBarButtonFactory()
975 */
976 protected ButtonFactory getToolBarButtonFactory() {
977 return getCommandServices().getToolBarButtonFactory();
978 }
979
980 /**
981 * @see CommandServices#getMenuItemButtonConfigurer()
982 */
983 protected CommandButtonConfigurer getMenuItemButtonConfigurer() {
984 return getCommandServices().getMenuItemButtonConfigurer();
985 }
986
987 /**
988 * @see CommandServices#getComponentFactory()
989 */
990 protected ComponentFactory getComponentFactory() {
991 return getCommandServices().getComponentFactory();
992 }
993
994 /**
995 * @see CommandServices#getButtonFactory()
996 */
997 protected ButtonFactory getButtonFactory() {
998 return getCommandServices().getButtonFactory();
999 }
1000
1001 /**
1002 * @see CommandServices#getMenuFactory()
1003 */
1004 protected MenuFactory getMenuFactory() {
1005 return getCommandServices().getMenuFactory();
1006 }
1007
1008 /**
1009 * Search for a button representing this command in the provided container
1010 * and let it request the focus.
1011 *
1012 * @param container the container which holds the command button.
1013 * @return <code>true</code> if the focus request is likely to succeed.
1014 *
1015 * @see #getButtonIn(Container)
1016 * @see JComponent#requestFocusInWindow()
1017 */
1018 public boolean requestFocusIn(Container container) {
1019 AbstractButton button = getButtonIn(container);
1020 if (button != null) {
1021 return button.requestFocusInWindow();
1022 }
1023 return false;
1024 }
1025
1026 /**
1027 * Search for the first button of this command that is a child component of
1028 * the given container.
1029 *
1030 * @param container the container to be searched.
1031 * @return the {@link AbstractButton} representing this command that is
1032 * embedded in the container or <code>null</code> if none was found.
1033 */
1034 public AbstractButton getButtonIn(Container container) {
1035 Iterator it = buttonIterator();
1036 while (it.hasNext()) {
1037 AbstractButton button = (AbstractButton) it.next();
1038 if (SwingUtilities.isDescendingFrom(button, container)) {
1039 return button;
1040 }
1041 }
1042 return null;
1043 }
1044
1045 /**
1046 * {@inheritDoc}
1047 */
1048 public String toString() {
1049 return new ToStringCreator(this).append("id", getId()).append("enabled", enabled).append("visible", visible)
1050 .append("defaultFaceDescriptorId", defaultFaceDescriptorId).toString();
1051 }
1052
1053 }