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.dialog; 017 018 import org.apache.commons.logging.Log; 019 import org.apache.commons.logging.LogFactory; 020 import org.springframework.richclient.application.support.ApplicationServicesAccessor; 021 import org.springframework.richclient.command.AbstractCommand; 022 import org.springframework.richclient.command.ActionCommand; 023 import org.springframework.richclient.command.CommandGroup; 024 import org.springframework.richclient.core.Guarded; 025 import org.springframework.richclient.core.TitleConfigurable; 026 import org.springframework.richclient.util.GuiStandardUtils; 027 import org.springframework.richclient.util.WindowUtils; 028 import org.springframework.util.Assert; 029 import org.springframework.util.StringUtils; 030 031 import javax.swing.*; 032 import java.awt.*; 033 import java.awt.event.KeyEvent; 034 import java.awt.event.WindowAdapter; 035 import java.awt.event.WindowEvent; 036 import java.awt.event.WindowFocusListener; 037 038 /** 039 * <p> 040 * Abstract Base Class for a dialog with standard layout, buttons, and behavior. 041 * </p> 042 * <p> 043 * Use of this class will apply a standard appearance to dialogs in the 044 * application. 045 * </p> 046 * <p> 047 * Subclasses implement the body of the dialog (wherein business objects are 048 * manipulated), and the action taken by the <code>OK</code> button. Aside 049 * from creating the dialog's contentj with {@link #createDialogContentPane()}, 050 * a proper disposing should be implemented in 051 * {@link #disposeDialogContentPane()}. 052 * </p> 053 * <p> 054 * Services of a <code>ApplicationDialog</code> include: 055 * <ul> 056 * <li>centering on the parent frame</li> 057 * <li>reusing the parent's icon</li> 058 * <li>standard layout and border spacing, based on Java Look and Feel 059 * guidelines.</li> 060 * <li>uniform naming style for dialog title</li> 061 * <li><code>OK</code> and <code>Cancel</code> buttons at the bottom of the 062 * dialog -<code>OK</code> is the default, and the <code>Escape</code> key 063 * activates <code>Cancel</code> (the latter works only if the dialog receives 064 * the escape keystroke, and not one of its components)</li> 065 * <li>by default, modal</li> 066 * <li>enabling & disabling of resizing</li> 067 * <li>will be shown in taskbar if no parent window has been set, and no 068 * applicationwindow is open</li> 069 * </ul> 070 * </p> 071 * <em>Note: Default close behaviour is to dispose the graphical dialog when it closes, you can set the CloseAction to hide if needed.</em> 072 * 073 * @author Keith Donald 074 * @author Jan Hoskens 075 */ 076 public abstract class ApplicationDialog extends ApplicationServicesAccessor implements TitleConfigurable, Guarded { 077 078 private static final String DEFAULT_DIALOG_TITLE = "Application Dialog"; 079 080 protected static final String DEFAULT_FINISH_COMMAND_ID = "okCommand"; 081 082 protected static final String DEFAULT_CANCEL_COMMAND_ID = "cancelCommand"; 083 084 protected static final String DEFAULT_FINISH_SUCCESS_MESSAGE_KEY = "defaultFinishSuccessMessage"; 085 086 protected static final String DEFAULT_FINISH_SUCCESS_TITLE_KEY = "defaultFinishSuccessTitle"; 087 088 protected static final String SUCCESS_FINISH_MESSAGE_KEY = "finishSuccessMessage"; 089 090 protected static final String SUCCESS_FINISH_TITLE_KEY = "finishSuccessTitle"; 091 092 protected final Log logger = LogFactory.getLog(getClass()); 093 094 private final DialogEventHandler dialogEventHandler = new DialogEventHandler(); 095 096 private String title; 097 098 private JDialog dialog; 099 100 private Component parentComponent; 101 102 private Window parentWindow; 103 104 private CloseAction closeAction = CloseAction.DISPOSE; 105 106 private boolean defaultEnabled = true; 107 108 private boolean modal = true; 109 110 private boolean resizable = true; 111 112 private Dimension preferredSize; 113 114 private Point location; 115 116 private Component locationRelativeTo; 117 118 private ActionCommand finishCommand; 119 120 private ActionCommand cancelCommand; 121 122 private CommandGroup dialogCommandGroup; 123 124 private boolean displayFinishSuccessMessage; 125 126 private ActionCommand callingCommand; 127 128 /** 129 * Create dialog with default closeAction {@link CloseAction#DISPOSE}. No 130 * parent or title set. 131 * 132 * @see #init() 133 */ 134 public ApplicationDialog() { 135 init(); 136 } 137 138 /** 139 * Create dialog with default closeAction {@link CloseAction#DISPOSE}. 140 * 141 * @param title text that will appear on dialog's titlebar. 142 * @param parent component serving as parent in it's hierarchy. 143 * 144 * @see #init() 145 */ 146 public ApplicationDialog(String title, Component parent) { 147 setTitle(title); 148 setParentComponent(parent); 149 init(); 150 } 151 152 /** 153 * Creates a new application dialog. The actual UI is not initialized until 154 * showDialog() is called. 155 * 156 * @param title text which appears in the title bar after the name of the 157 * application. 158 * @param parent frame to which this dialog is attached. 159 * @param closeAction sets the behaviour of the dialog upon close. Default 160 * closeAction is {@link CloseAction#DISPOSE}. 161 * 162 * @see #init() 163 */ 164 public ApplicationDialog(String title, Component parent, CloseAction closeAction) { 165 setTitle(title); 166 setParentComponent(parent); 167 setCloseAction(closeAction); 168 init(); 169 } 170 171 /** 172 * Hook called in constructor. Add specific initialization code here. 173 */ 174 protected void init() { 175 } 176 177 /** 178 * {@inheritDoc} 179 */ 180 public void setTitle(String title) { 181 this.title = title; 182 if (dialog != null) { 183 dialog.setTitle(getTitle()); 184 } 185 } 186 187 /** 188 * Returns the title of this dialog. If no specific title has been set, the 189 * calling command's text will be used. If that doesn't yield a result, the 190 * default title is returned. 191 * 192 * @see #getCallingCommandText() 193 * @see #DEFAULT_DIALOG_TITLE 194 */ 195 protected String getTitle() { 196 if (!StringUtils.hasText(this.title)) { 197 if (StringUtils.hasText(getCallingCommandText())) 198 return getCallingCommandText(); 199 200 return DEFAULT_DIALOG_TITLE; 201 } 202 return this.title; 203 } 204 205 /** 206 * The parent Component that will be used to extract the Frame/Dialog owner 207 * for the JDialog at creation. You may pass a Window/Frame that will be 208 * used directly as parent for the JDialog, or you can pass the component 209 * which has one of both in it's parent hierarchy. The latter option can be 210 * handy when you're locally implementing Components without a direct 211 * -connection to/notion of- a Window/Frame. 212 * 213 * @param parentComponent Component that is a Frame/Window or has one in its 214 * parent hierarchy. 215 */ 216 public void setParentComponent(Component parentComponent) { 217 this.parentComponent = parentComponent; 218 } 219 220 /** 221 * Returns the parent Component. 222 * 223 * @return 224 * @see #setParentComponent(Component) 225 */ 226 public Component getParentComponent() { 227 return this.parentComponent; 228 } 229 230 /** 231 * Set the {@link CloseAction} of this dialog. Default action is 232 * {@link CloseAction#DISPOSE} which disposes the visual dialog upon 233 * closing. When using {@link CloseAction#HIDE} the visual components are 234 * cached and reused. 235 * 236 * @param action the {@link CloseAction} to use when closing the dialog. 237 */ 238 public void setCloseAction(CloseAction action) { 239 this.closeAction = action; 240 } 241 242 /** 243 * When opening the dialog, the finish button can be enabled by default. 244 * 245 * @param enabled <code>true</code> when the finish button should be 246 * enabled by default, <code>false</code> otherwise. 247 */ 248 public void setDefaultEnabled(boolean enabled) { 249 this.defaultEnabled = enabled; 250 } 251 252 /** 253 * Set the modal property of the dialog. 254 * 255 * @see JDialog#setModal(boolean) 256 */ 257 public void setModal(boolean modal) { 258 this.modal = modal; 259 } 260 261 /** 262 * Set the resizable property of the dialog. 263 * 264 * @see JDialog#setResizable(boolean) 265 */ 266 public void setResizable(boolean resizable) { 267 this.resizable = resizable; 268 } 269 270 /** 271 * Set a specific location for the JDialog to popup. 272 * 273 * @param location point on screen where to place the JDialog. 274 */ 275 public void setLocation(Point location) { 276 this.location = location; 277 } 278 279 /** 280 * Set a relative location for the JDialog to popup. 281 * 282 * @see Window#setLocationRelativeTo(Component) 283 */ 284 public void setLocationRelativeTo(Component locationRelativeTo) { 285 this.locationRelativeTo = locationRelativeTo; 286 } 287 288 /** 289 * Set the preferrred size for the JDialog. 290 * 291 * @see JComponent#setPreferredSize(Dimension) 292 */ 293 public void setPreferredSize(Dimension preferredSize) { 294 this.preferredSize = preferredSize; 295 } 296 297 /** 298 * Enable/disable the finish command of the dialog. 299 */ 300 public void setEnabled(boolean enabled) { 301 setFinishEnabled(enabled); 302 } 303 304 /** 305 * Message to show upon succesful completion. 306 */ 307 public void setDisplayFinishSuccessMessage(boolean displayFinishSuccessMessage) { 308 this.displayFinishSuccessMessage = displayFinishSuccessMessage; 309 } 310 311 /** 312 * Set the command that opened this dialog. 313 * 314 * @see #getFinishSuccessMessage() 315 * @see #getFinishSuccessTitle() 316 */ 317 public void setCallingCommand(ActionCommand callingCommand) { 318 this.callingCommand = callingCommand; 319 } 320 321 /** 322 * Enable/disable the finish command. 323 */ 324 protected void setFinishEnabled(boolean enabled) { 325 if (isControlCreated()) { 326 finishCommand.setEnabled(enabled); 327 } 328 } 329 330 /** 331 * Returns whether this Dialog is enabled. 332 */ 333 public boolean isEnabled() { 334 if (isControlCreated()) 335 return finishCommand.isEnabled(); 336 337 return false; 338 } 339 340 /** 341 * Returns <code>true</code> if the JDialog is showing. 342 * 343 * @see JDialog#isShowing() 344 */ 345 public boolean isShowing() { 346 if (!isControlCreated()) { 347 return false; 348 } 349 return dialog.isShowing(); 350 } 351 352 /** 353 * Returns <code>true</code> if the JDialog is constructed. 354 */ 355 public boolean isControlCreated() { 356 return dialog != null; 357 } 358 359 /** 360 * Return the JDialog, create it if needed (lazy). 361 */ 362 public JDialog getDialog() { 363 if (!isControlCreated()) { 364 createDialog(); 365 } 366 return dialog; 367 } 368 369 /** 370 * Return the contentPane of the dialog. 371 * 372 * @see JDialog#getContentPane() 373 */ 374 protected Container getDialogContentPane() { 375 Assert.state(isControlCreated(), "The wrapped JDialog control has not yet been created."); 376 return dialog.getContentPane(); 377 } 378 379 /** 380 * <p> 381 * Show the dialog. The dialog will be created if it doesn't exist yet. 382 * Before setting the dialog visible, a hook method onAboutToShow is called 383 * and the location will be set. 384 * </p> 385 * <p> 386 * When showing the dialog several times, it will always be opened on the 387 * location that has been set, or relative to the parent. (former location 388 * will not persist) 389 * </p> 390 */ 391 public void showDialog() { 392 if (!isControlCreated()) { 393 createDialog(); 394 } 395 if (!isShowing()) { 396 onAboutToShow(); 397 if (getLocation() != null) { 398 dialog.setLocation(getLocation()); 399 dialog.setPreferredSize(getPreferredSize()); 400 } 401 else { 402 WindowUtils.centerOnParent(dialog, getLocationRelativeTo()); 403 } 404 405 dialog.setVisible(true); 406 } 407 } 408 409 /** 410 * Subclasses should call if layout of the dialog components changes. 411 */ 412 protected void componentsChanged() { 413 if (isControlCreated()) { 414 dialog.pack(); 415 } 416 } 417 418 /** 419 * Builds/initializes the dialog and all of its components. 420 * <p> 421 * Follows the Java Look and Feel guidelines for spacing elements. 422 */ 423 protected final void createDialog() { 424 constructDialog(); 425 addDialogComponents(); 426 attachListeners(); 427 registerDefaultCommand(); 428 onInitialized(); 429 430 dialog.pack(); 431 } 432 433 /** 434 * Construct the visual dialog frame on which the content needs to be added. 435 */ 436 private void constructDialog() { 437 if (getParentWindow() instanceof Frame) { 438 dialog = new JDialog((Frame) getParentWindow(), getTitle(), modal); 439 } 440 else { 441 dialog = new JDialog((Dialog) getParentWindow(), getTitle(), modal); 442 } 443 444 dialog.getContentPane().setLayout(new BorderLayout()); 445 dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 446 dialog.setResizable(resizable); 447 448 initStandardCommands(); 449 addCancelByEscapeKey(); 450 } 451 452 /** 453 * <p> 454 * --jh-- This method is copied from JOptionPane. I'm still trying to figure 455 * out why they chose to have a static method with package visibility for 456 * this one instead of just making it public. 457 * </p> 458 * 459 * Returns the specified component's toplevel <code>Frame</code> or 460 * <code>Dialog</code>. 461 * 462 * @param parentComponent the <code>Component</code> to check for a 463 * <code>Frame</code> or <code>Dialog</code> 464 * @return the <code>Frame</code> or <code>Dialog</code> that contains 465 * the component, or the default frame if the component is <code>null</code>, 466 * or does not have a valid <code>Frame</code> or <code>Dialog</code> 467 * parent 468 * @exception HeadlessException if 469 * <code>GraphicsEnvironment.isHeadless</code> returns <code>true</code> 470 * @see java.awt.GraphicsEnvironment#isHeadless 471 */ 472 public static Window getWindowForComponent(Component parentComponent) throws HeadlessException { 473 if (parentComponent == null) 474 return JOptionPane.getRootFrame(); 475 if (parentComponent instanceof Frame || parentComponent instanceof Dialog) 476 return (Window) parentComponent; 477 return getWindowForComponent(parentComponent.getParent()); 478 } 479 480 /** 481 * Initialize the standard commands needed on a Dialog: Ok/Cancel. 482 */ 483 private void initStandardCommands() { 484 finishCommand = new ActionCommand(getFinishCommandId()) { 485 public void doExecuteCommand() { 486 boolean result = onFinish(); 487 if (result) { 488 if (getDisplayFinishSuccessMessage()) { 489 showFinishSuccessMessageDialog(); 490 } 491 executeCloseAction(); 492 } 493 } 494 }; 495 finishCommand.setSecurityControllerId(getFinishSecurityControllerId()); 496 finishCommand.setEnabled(defaultEnabled); 497 498 cancelCommand = new ActionCommand(getCancelCommandId()) { 499 500 public void doExecuteCommand() { 501 onCancel(); 502 } 503 }; 504 } 505 506 /** 507 * Subclasses may override to return a custom message key, default is 508 * "okCommand", corresponding to the "&OK" label. 509 * 510 * @return The message key to use for the finish ("ok") button 511 */ 512 protected String getFinishCommandId() { 513 return DEFAULT_FINISH_COMMAND_ID; 514 } 515 516 /** 517 * Subclasses may override to return a security controller id to be attached 518 * to the finish command. The default is null, no controller. 519 * 520 * @return security controller id, or null if none 521 */ 522 protected String getFinishSecurityControllerId() { 523 return null; 524 } 525 526 /** 527 * Request invocation of the action taken when the user hits the 528 * <code>OK</code> (finish) button. 529 * 530 * @return true if action completed successfully; false otherwise. 531 */ 532 protected abstract boolean onFinish(); 533 534 /** 535 * Return the message that needs to be set on a succesful finish. 536 */ 537 protected boolean getDisplayFinishSuccessMessage() { 538 return displayFinishSuccessMessage; 539 } 540 541 /** 542 * Opens a dialog which contains the sussesful finish message. 543 * 544 * @see #getFinishSuccessTitle() 545 * @see #getFinishSuccessMessage() 546 */ 547 protected void showFinishSuccessMessageDialog() { 548 MessageDialog messageDialog = new MessageDialog(getFinishSuccessTitle(), getDialog(), getFinishSuccessMessage()); 549 messageDialog.showDialog(); 550 } 551 552 /** 553 * Returns the message to use upon succesful finish. 554 */ 555 protected String getFinishSuccessMessage() { 556 ActionCommand callingCommand = getCallingCommand(); 557 if (callingCommand != null) { 558 String[] successMessageKeys = new String[] { callingCommand.getId() + "." + SUCCESS_FINISH_MESSAGE_KEY, 559 DEFAULT_FINISH_SUCCESS_MESSAGE_KEY }; 560 return getMessage(successMessageKeys, getFinishSuccessMessageArguments()); 561 } 562 return getMessage(DEFAULT_FINISH_SUCCESS_MESSAGE_KEY); 563 } 564 565 /** 566 * Returns the command that opened this dialog. 567 */ 568 protected ActionCommand getCallingCommand() { 569 return callingCommand; 570 } 571 572 /** 573 * Returns the arguments to use in the succesful finish message. 574 */ 575 protected Object[] getFinishSuccessMessageArguments() { 576 return new Object[0]; 577 } 578 579 /** 580 * Returns the title to use upon succesful finish. 581 */ 582 protected String getFinishSuccessTitle() { 583 ActionCommand callingCommand = getCallingCommand(); 584 if (callingCommand != null) { 585 String[] successTitleKeys = new String[] { callingCommand.getId() + "." + SUCCESS_FINISH_TITLE_KEY, 586 DEFAULT_FINISH_SUCCESS_TITLE_KEY }; 587 return getMessage(successTitleKeys, getFinishSuccessTitleArguments()); 588 } 589 return getMessage(DEFAULT_FINISH_SUCCESS_TITLE_KEY); 590 } 591 592 /** 593 * Returns the arguments to use in the finish succesful title. 594 */ 595 protected Object[] getFinishSuccessTitleArguments() { 596 if (StringUtils.hasText(getCallingCommandText())) 597 return new Object[] { getCallingCommandText() }; 598 599 return new Object[0]; 600 } 601 602 /** 603 * Return the text of the command that opened this dialog. 604 */ 605 private String getCallingCommandText() { 606 return getCallingCommand() != null ? getCallingCommand().getText() : null; 607 } 608 609 /** 610 * Returns the id for the cancel command. 611 */ 612 protected String getCancelCommandId() { 613 return DEFAULT_CANCEL_COMMAND_ID; 614 } 615 616 /** 617 * Returns the finish command. 618 */ 619 protected ActionCommand getFinishCommand() { 620 return finishCommand; 621 } 622 623 /** 624 * Returns the cancel command. 625 */ 626 protected ActionCommand getCancelCommand() { 627 return cancelCommand; 628 } 629 630 /** 631 * Force the escape key to call the same action as pressing the Cancel 632 * button. This does not always work. See class comment. 633 */ 634 private void addCancelByEscapeKey() { 635 int noModifiers = 0; 636 KeyStroke escapeKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, noModifiers, false); 637 addActionKeyBinding(escapeKey, cancelCommand.getId()); 638 } 639 640 /** 641 * Add an action key binding to this dialog. 642 * 643 * @param key the {@link KeyStroke} that triggers the command/action. 644 * @param actionKey id of command that will be triggered by the {@link KeyStroke}. 645 * 646 * @see #addActionKeyBinding(KeyStroke, String, Action) 647 */ 648 protected void addActionKeyBinding(KeyStroke key, String actionKey) { 649 if (actionKey == finishCommand.getId()) { 650 addActionKeyBinding(key, actionKey, finishCommand.getActionAdapter()); 651 } 652 else if (actionKey == cancelCommand.getId()) { 653 addActionKeyBinding(key, actionKey, cancelCommand.getActionAdapter()); 654 } 655 else { 656 throw new IllegalArgumentException("Unknown action key " + actionKey); 657 } 658 } 659 660 /** 661 * Add an action key binding to this dialog. 662 * 663 * @param key the {@link KeyStroke} that triggers the command/action. 664 * @param actionKey id of the action. 665 * @param action {@link Action} that will be triggered by the {@link KeyStroke}. 666 * 667 * @see #getActionMap() 668 * @see #getInputMap() 669 * @see ActionMap#put(Object, Action) 670 * @see InputMap#put(KeyStroke, Object) 671 */ 672 protected void addActionKeyBinding(KeyStroke key, String actionKey, Action action) { 673 getInputMap().put(key, actionKey); 674 getActionMap().put(actionKey, action); 675 } 676 677 /** 678 * Return the {@link ActionMap} of the dialog. 679 * 680 * @see JLayeredPane#getActionMap() 681 */ 682 protected ActionMap getActionMap() { 683 return getDialog().getLayeredPane().getActionMap(); 684 } 685 686 /** 687 * Return the {@link InputMap} of the dialog. 688 * 689 * @see JLayeredPane#getInputMap(int) 690 */ 691 protected InputMap getInputMap() { 692 return getDialog().getLayeredPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 693 } 694 695 /** 696 * Subclasses may override to customize how this dialog is built. 697 */ 698 protected void addDialogComponents() { 699 JComponent dialogContentPane = createDialogContentPane(); 700 GuiStandardUtils.attachDialogBorder(dialogContentPane); 701 if (getPreferredSize() != null) { 702 dialogContentPane.setPreferredSize(getPreferredSize()); 703 } 704 getDialogContentPane().add(dialogContentPane); 705 getDialogContentPane().add(createButtonBar(), BorderLayout.SOUTH); 706 } 707 708 /** 709 * Return the location of the dialog. 710 */ 711 protected Point getLocation() { 712 return location; 713 } 714 715 /** 716 * Return the relative location of the dialog. 717 */ 718 protected Component getLocationRelativeTo() { 719 return locationRelativeTo; 720 } 721 722 /** 723 * Return the preferred size for the dialog. 724 */ 725 protected Dimension getPreferredSize() { 726 return preferredSize; 727 } 728 729 /** 730 * Return the GUI which allows the user to manipulate the business objects 731 * related to this dialog. This GUI will be placed above the <code>OK</code> 732 * and <code>Cancel</code> buttons, in a standard manner. 733 * 734 * <p> 735 * Any components/objects created at this point need to be disposed in 736 * {@link #disposeDialogContentPane()}. 737 * </p> 738 * 739 * @see #disposeDialogContentPane() 740 */ 741 protected abstract JComponent createDialogContentPane(); 742 743 /** 744 * Attach the handler that invokes the lifecycle methods on the 745 * <code>ApplicationDialog</code>. 746 * 747 * @see DialogEventHandler 748 */ 749 protected final void attachListeners() { 750 dialog.addWindowFocusListener(dialogEventHandler); 751 dialog.addWindowListener(dialogEventHandler); 752 } 753 754 /** 755 * Return a standardized row of command buttons, right-justified and all of 756 * the same size, with OK as the default button, and no mnemonics used, as 757 * per the Java Look and Feel guidelines. 758 */ 759 protected JComponent createButtonBar() { 760 this.dialogCommandGroup = CommandGroup.createCommandGroup(null, getCommandGroupMembers()); 761 JComponent buttonBar = this.dialogCommandGroup.createButtonBar(); 762 GuiStandardUtils.attachDialogBorder(buttonBar); 763 return buttonBar; 764 } 765 766 /** 767 * Template getter method to return the commands to populate the dialog 768 * button bar. 769 * 770 * @return The array of commands (may also be a separator or glue 771 * identifier) 772 */ 773 protected Object[] getCommandGroupMembers() { 774 return new AbstractCommand[] { getFinishCommand(), getCancelCommand() }; 775 } 776 777 /** 778 * Register the finish button as the default dialog button. 779 */ 780 protected void registerDefaultCommand() { 781 if (isControlCreated()) { 782 finishCommand.setDefaultButtonIn(getDialog()); 783 } 784 } 785 786 /** 787 * Register the cancel button as the default dialog button. 788 */ 789 protected final void registerCancelCommandAsDefault() { 790 if (isControlCreated()) { 791 cancelCommand.setDefaultButtonIn(getDialog()); 792 } 793 } 794 795 /** 796 * Register the provided button as the default dialog button. The button 797 * must be present on the dialog. 798 * 799 * @param command The button to become the default. 800 */ 801 protected final void registerDefaultCommand(ActionCommand command) { 802 if (isControlCreated()) { 803 command.setDefaultButtonIn(getDialog()); 804 } 805 } 806 807 /** 808 * Template lifecycle method invoked after the dialog control is 809 * initialized. 810 */ 811 protected void onInitialized() { 812 } 813 814 /** 815 * Template lifecycle method invoked right before the dialog is to become 816 * visible. 817 */ 818 protected void onAboutToShow() { 819 } 820 821 /** 822 * Template lifecycle method invoked when the dialog gains focus. 823 */ 824 protected void onWindowGainedFocus() { 825 } 826 827 /** 828 * Template lifecycle method invoked when the dialog is activated. 829 */ 830 protected void onWindowActivated() { 831 } 832 833 /** 834 * Template lifecycle method invoked when the dialog loses focus. 835 */ 836 protected void onWindowLostFocus() { 837 } 838 839 /** 840 * Template lifecycle method invoked when the dialog's window is closing. 841 */ 842 protected void onWindowClosing() { 843 } 844 845 /** 846 * Handle a dialog cancellation request. 847 */ 848 protected void onCancel() { 849 executeCloseAction(); 850 } 851 852 /** 853 * Select the appropriate close logic. 854 */ 855 private void executeCloseAction() { 856 if (closeAction == CloseAction.HIDE) { 857 hide(); 858 } 859 else { 860 dispose(); 861 } 862 } 863 864 /** 865 * Close and dispose of the visual dialog. This forces the dialog to be 866 * re-built on the next show. Any subclasses that are creating visual 867 * components and holding references to them should dispose them when the 868 * surrounding dialog is disposed by implementing 869 * {@link #disposeDialogContentPane()}. Any other objects that are created 870 * in {@link #createDialogContentPane()} can be handled here as well. 871 * 872 * @see #disposeDialogContentPane() 873 */ 874 protected final void dispose() { 875 if (dialog != null) { 876 onWindowClosing(); 877 disposeDialogContentPane(); 878 dialog.dispose(); 879 dialog = null; 880 } 881 } 882 883 /** 884 * Cleanup any components/objects that are created during 885 * {@link #createDialogContentPane()}. This method is called if the 886 * {@link CloseAction} is set to {@link CloseAction#DISPOSE} and the dialog 887 * is being closed. This ensures that when disposing the surrounding dialog, 888 * the content pane can be disposed as well. 889 * 890 * @see #createDialogContentPane() 891 * @see #dispose() 892 */ 893 protected void disposeDialogContentPane() { 894 } 895 896 /** 897 * Hide the dialog. This differs from dispose in that the dialog control 898 * stays cached in memory. 899 */ 900 protected final void hide() { 901 if (dialog != null) { 902 onWindowClosing(); 903 this.dialog.setVisible(false); 904 } 905 } 906 907 /** 908 * Returns the parent window based on the internal parent Component. Will 909 * search for a Window in the parent hierarchy if needed (when parent 910 * Component isn't a Window). 911 * 912 * @return the parent window 913 */ 914 public Window getParentWindow() { 915 if (parentWindow == null) { 916 if ((parentComponent == null) && (getActiveWindow() != null)) { 917 parentWindow = getActiveWindow().getControl(); 918 } 919 else { 920 parentWindow = getWindowForComponent(parentComponent); 921 } 922 } 923 return parentWindow; 924 } 925 926 /** 927 * Handler that will be registered as listener on the dialog. 928 */ 929 private class DialogEventHandler extends WindowAdapter implements WindowFocusListener { 930 public void windowActivated(WindowEvent e) { 931 onWindowActivated(); 932 } 933 934 public void windowClosing(WindowEvent e) { 935 getCancelCommand().execute(); 936 } 937 938 public void windowGainedFocus(WindowEvent e) { 939 onWindowGainedFocus(); 940 } 941 942 public void windowLostFocus(WindowEvent e) { 943 onWindowLostFocus(); 944 } 945 } 946 }