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 }