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 }