KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > dialogs > PopupDialog


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

12 package org.eclipse.jface.dialogs;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.jface.action.Action;
18 import org.eclipse.jface.action.GroupMarker;
19 import org.eclipse.jface.action.IAction;
20 import org.eclipse.jface.action.IMenuManager;
21 import org.eclipse.jface.action.MenuManager;
22 import org.eclipse.jface.action.Separator;
23 import org.eclipse.jface.layout.GridDataFactory;
24 import org.eclipse.jface.layout.GridLayoutFactory;
25 import org.eclipse.jface.resource.ImageDescriptor;
26 import org.eclipse.jface.resource.JFaceResources;
27 import org.eclipse.jface.window.Window;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.events.DisposeEvent;
30 import org.eclipse.swt.events.DisposeListener;
31 import org.eclipse.swt.events.MouseAdapter;
32 import org.eclipse.swt.events.MouseEvent;
33 import org.eclipse.swt.events.SelectionAdapter;
34 import org.eclipse.swt.events.SelectionEvent;
35 import org.eclipse.swt.graphics.Color;
36 import org.eclipse.swt.graphics.Font;
37 import org.eclipse.swt.graphics.FontData;
38 import org.eclipse.swt.graphics.Image;
39 import org.eclipse.swt.graphics.Point;
40 import org.eclipse.swt.graphics.Rectangle;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.Control;
43 import org.eclipse.swt.widgets.Display;
44 import org.eclipse.swt.widgets.Event;
45 import org.eclipse.swt.widgets.Label;
46 import org.eclipse.swt.widgets.Listener;
47 import org.eclipse.swt.widgets.Menu;
48 import org.eclipse.swt.widgets.Shell;
49 import org.eclipse.swt.widgets.ToolBar;
50 import org.eclipse.swt.widgets.ToolItem;
51 import org.eclipse.swt.widgets.Tracker;
52
53 /**
54  * A lightweight, transient dialog that is popped up to show contextual or
55  * temporal information and is easily dismissed. Clients control whether the
56  * dialog should be able to receive input focus. An optional title area at the
57  * top and an optional info area at the bottom can be used to provide additional
58  * information.
59  * <p>
60  * Because the dialog is short-lived, most of the configuration of the dialog is
61  * done in the constructor. Set methods are only provided for those values that
62  * are expected to be dynamically computed based on a particular instance's
63  * internal state.
64  * <p>
65  * Clients are expected to override the creation of the main dialog area, and
66  * may optionally override the creation of the title area and info area in order
67  * to add content. In general, however, the creation of stylistic features, such
68  * as the dialog menu, separator styles, and fonts, is kept private so that all
69  * popup dialogs will have a similar appearance.
70  *
71  * @since 3.2
72  */

73 public class PopupDialog extends Window {
74
75     /**
76      *
77      */

78     private static final GridDataFactory LAYOUTDATA_GRAB_BOTH = GridDataFactory.fillDefaults().grab(true,true);
79
80     /**
81      * The dialog settings key name for stored dialog x location.
82      */

83     private static final String JavaDoc DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$
84

85     /**
86      * The dialog settings key name for stored dialog y location.
87      */

88     private static final String JavaDoc DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$
89

90     /**
91      * The dialog settings key name for stored dialog width.
92      */

93     private static final String JavaDoc DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
94

95     /**
96      * The dialog settings key name for stored dialog height.
97      */

98     private static final String JavaDoc DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
99

100     /**
101      * The dialog settings key name for remembering if the persisted bounds
102      * should be accessed.
103      */

104     private static final String JavaDoc DIALOG_USE_PERSISTED_BOUNDS = "DIALOG_USE_PERSISTED_BOUNDS"; //$NON-NLS-1$
105

106     /**
107      * Move action for the dialog.
108      */

109     private class MoveAction extends Action {
110
111         MoveAction() {
112             super(JFaceResources.getString("PopupDialog.move"), //$NON-NLS-1$
113
IAction.AS_PUSH_BUTTON);
114         }
115
116         /*
117          * (non-Javadoc)
118          *
119          * @see org.eclipse.jface.action.IAction#run()
120          */

121         public void run() {
122             performTrackerAction(SWT.NONE);
123         }
124
125     }
126
127     /**
128      * Resize action for the dialog.
129      */

130     private class ResizeAction extends Action {
131
132         ResizeAction() {
133             super(JFaceResources.getString("PopupDialog.resize"), //$NON-NLS-1$
134
IAction.AS_PUSH_BUTTON);
135         }
136
137         /*
138          * @see org.eclipse.jface.action.Action#run()
139          */

140         public void run() {
141             performTrackerAction(SWT.RESIZE);
142         }
143     }
144
145     /**
146      *
147      * Remember bounds action for the dialog.
148      */

149     private class PersistBoundsAction extends Action {
150
151         PersistBoundsAction() {
152             super(JFaceResources.getString("PopupDialog.persistBounds"), //$NON-NLS-1$
153
IAction.AS_CHECK_BOX);
154             setChecked(persistBounds);
155         }
156
157         /*
158          * (non-Javadoc)
159          *
160          * @see org.eclipse.jface.action.IAction#run()
161          */

162         public void run() {
163             persistBounds = isChecked();
164         }
165     }
166
167     /**
168      * Shell style appropriate for a simple hover popup that cannot get focus.
169      */

170     public final static int HOVER_SHELLSTYLE = SWT.NO_FOCUS | SWT.ON_TOP
171             | SWT.NO_TRIM;
172
173     /**
174      * Shell style appropriate for an info popup that can get focus.
175      */

176     public final static int INFOPOPUP_SHELLSTYLE = SWT.NO_TRIM;
177
178     /**
179      * Shell style appropriate for a resizable info popup that can get focus.
180      */

181     public final static int INFOPOPUPRESIZE_SHELLSTYLE = SWT.RESIZE;
182
183     /**
184      * Margin width (in pixels) to be used in layouts inside popup dialogs
185      * (value is 0).
186      */

187     public final static int POPUP_MARGINWIDTH = 0;
188
189     /**
190      * Margin height (in pixels) to be used in layouts inside popup dialogs
191      * (value is 0).
192      */

193     public final static int POPUP_MARGINHEIGHT = 0;
194
195     /**
196      * Vertical spacing (in pixels) between cells in the layouts inside popup
197      * dialogs (value is 1).
198      */

199     public final static int POPUP_VERTICALSPACING = 1;
200
201     /**
202      * Vertical spacing (in pixels) between cells in the layouts inside popup
203      * dialogs (value is 1).
204      */

205     public final static int POPUP_HORIZONTALSPACING = 1;
206
207     /**
208      *
209      */

210     private static final GridLayoutFactory POPUP_LAYOUT_FACTORY = GridLayoutFactory
211             .fillDefaults().margins(POPUP_MARGINWIDTH, POPUP_MARGINHEIGHT)
212             .spacing(POPUP_HORIZONTALSPACING, POPUP_VERTICALSPACING);
213     
214     /**
215      * Border thickness in pixels.
216      */

217     private static final int BORDER_THICKNESS = 1;
218
219     /**
220      * The dialog's toolbar for the move and resize capabilities.
221      */

222     private ToolBar toolBar = null;
223
224     /**
225      * The dialog's menu manager.
226      */

227     private MenuManager menuManager = null;
228
229     /**
230      * The control representing the main dialog area.
231      */

232     private Control dialogArea;
233
234     /**
235      * Labels that contain title and info text. Cached so they can be updated
236      * dynamically if possible.
237      */

238     private Label titleLabel, infoLabel;
239
240     /**
241      * Separator controls. Cached so they can be excluded from color changes.
242      */

243     private Control titleSeparator, infoSeparator;
244
245     /**
246      * The images for the dialog menu.
247      */

248     private Image menuImage, disabledMenuImage = null;
249
250     /**
251      * Font to be used for the info area text. Computed based on the dialog's
252      * font.
253      */

254     private Font infoFont;
255     
256     /**
257      * Font to be used for the title area text. Computed based on the dialog's
258      * font.
259      */

260     private Font titleFont;
261
262     /**
263      * Flags indicating whether we are listening for shell deactivate events,
264      * either those or our parent's. Used to prevent closure when a menu command
265      * is chosen or a secondary popup is launched.
266      */

267     private boolean listenToDeactivate;
268
269     private boolean listenToParentDeactivate;
270     
271     private Listener parentDeactivateListener;
272
273     /**
274      * Flag indicating whether focus should be taken when the dialog is opened.
275      */

276     private boolean takeFocusOnOpen = false;
277
278     /**
279      * Flag specifying whether a menu should be shown that allows the user to
280      * move and resize.
281      */

282     private boolean showDialogMenu = false;
283
284     /**
285      * Flag specifying whether a menu action allowing the user to choose whether
286      * the dialog bounds should be persisted is to be shown.
287      */

288     private boolean showPersistAction = false;
289
290     /**
291      * Flag specifying whether the bounds of the popup should be persisted. This
292      * flag is updated by a menu if the menu is shown.
293      */

294     private boolean persistBounds = false;
295
296     /**
297      * Text to be shown in an optional title area (on top).
298      */

299     private String JavaDoc titleText;
300
301     /**
302      * Text to be shown in an optional info area (at the bottom).
303      */

304     private String JavaDoc infoText;
305
306     /**
307      * Constructs a new instance of <code>PopupDialog</code>.
308      *
309      * @param parent
310      * The parent shell.
311      * @param shellStyle
312      * The shell style.
313      * @param takeFocusOnOpen
314      * A boolean indicating whether focus should be taken by this
315      * popup when it opens.
316      * @param persistBounds
317      * A boolean indicating whether the bounds should be persisted
318      * upon close of the dialog. The bounds can only be persisted if
319      * the dialog settings for persisting the bounds are also
320      * specified. If a menu action will be provided that allows the
321      * user to control this feature, then the last known value of the
322      * user's setting will be used instead of this flag.
323      * @param showDialogMenu
324      * A boolean indicating whether a menu for moving and resizing
325      * the popup should be provided.
326      * @param showPersistAction
327      * A boolean indicating whether an action allowing the user to
328      * control the persisting of the dialog bounds should be shown in
329      * the dialog menu. This parameter has no effect if
330      * <code>showDialogMenu</code> is <code>false</code>.
331      * @param titleText
332      * Text to be shown in an upper title area, or <code>null</code>
333      * if there is no title.
334      * @param infoText
335      * Text to be shown in a lower info area, or <code>null</code>
336      * if there is no info area.
337      *
338      * @see PopupDialog#getDialogSettings()
339      */

340     public PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen,
341             boolean persistBounds, boolean showDialogMenu,
342             boolean showPersistAction, String JavaDoc titleText, String JavaDoc infoText) {
343         super(parent);
344         setShellStyle(shellStyle);
345         this.takeFocusOnOpen = takeFocusOnOpen;
346         this.showDialogMenu = showDialogMenu;
347         this.showPersistAction = showPersistAction;
348         this.titleText = titleText;
349         this.infoText = infoText;
350
351         setBlockOnOpen(false);
352
353         this.persistBounds = persistBounds;
354         initializeWidgetState();
355     }
356
357     /*
358      * (non-Javadoc)
359      *
360      * @see org.eclipse.jface.window.Window#configureShell(Shell)
361      */

362     protected void configureShell(Shell shell) {
363         Display display = shell.getDisplay();
364         shell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
365
366         int border = ((getShellStyle() & SWT.NO_TRIM) == 0) ? 0
367                 : BORDER_THICKNESS;
368         GridLayoutFactory.fillDefaults().margins(border, border).spacing(5,5).applyTo(shell);
369
370         shell.addListener(SWT.Deactivate, new Listener() {
371             public void handleEvent(Event event) {
372                 /*
373                  * Close if we are deactivating and have no child shells. If we
374                  * have child shells, we are deactivating due to their opening.
375                  * On X, we receive this when a menu child (such as the system
376                  * menu) of the shell opens, but I have not found a way to
377                  * distinguish that case here. Hence bug #113577 still exists.
378                  */

379                 if (listenToDeactivate && event.widget == getShell()
380                         && getShell().getShells().length == 0) {
381                     close();
382                 } else {
383                     /* We typically ignore deactivates to work around platform-specific
384                      * event ordering. Now that we've ignored whatever we were supposed to,
385                      * start listening to deactivates. Example issues can be found in
386                      * https://bugs.eclipse.org/bugs/show_bug.cgi?id=123392
387                      */

388                     listenToDeactivate = true;
389                 }
390             }
391         });
392         // Set this true whenever we activate. It may have been turned
393
// off by a menu or secondary popup showing.
394
shell.addListener(SWT.Activate, new Listener() {
395             public void handleEvent(Event event) {
396                 // ignore this event if we have launched a child
397
if (event.widget == getShell()
398                         && getShell().getShells().length == 0) {
399                     listenToDeactivate = true;
400                     // Typically we start listening for parent deactivate after
401
// we are activated, except on the Mac, where the deactivate
402
// is received after activate.
403
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=100668
404
listenToParentDeactivate = !"carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
405
}
406             }
407         });
408
409         if ((getShellStyle() & SWT.ON_TOP) != 0 && shell.getParent() != null) {
410             parentDeactivateListener= new Listener() {
411                 public void handleEvent(Event event) {
412                     if (listenToParentDeactivate) {
413                         close();
414                     } else {
415                         // Our first deactivate, now start listening on the Mac.
416
listenToParentDeactivate = listenToDeactivate;
417                     }
418                 }
419             };
420             shell.getParent().addListener(SWT.Deactivate, parentDeactivateListener);
421         }
422         
423         shell.addDisposeListener(new DisposeListener() {
424             public void widgetDisposed(DisposeEvent event) {
425                 handleDispose();
426             }
427         });
428     }
429
430     /**
431      * The <code>PopupDialog</code> implementation of this <code>Window</code>
432      * method creates and lays out the top level composite for the dialog. It
433      * then calls the <code>createTitleMenuArea</code>,
434      * <code>createDialogArea</code>, and <code>createInfoTextArea</code>
435      * methods to create an optional title and menu area on the top, a dialog
436      * area in the center, and an optional info text area at the bottom.
437      * Overriding <code>createDialogArea</code> and (optionally)
438      * <code>createTitleMenuArea</code> and <code>createTitleMenuArea</code>
439      * are recommended rather than overriding this method.
440      *
441      * @param parent
442      * the composite used to parent the contents.
443      *
444      * @return the control representing the contents.
445      */

446     protected Control createContents(Composite parent) {
447         Composite composite = new Composite(parent, SWT.NONE);
448         POPUP_LAYOUT_FACTORY.applyTo(composite);
449         LAYOUTDATA_GRAB_BOTH.applyTo(composite);
450
451         // Title area
452
if (hasTitleArea()) {
453             createTitleMenuArea(composite);
454             titleSeparator = createHorizontalSeparator(composite);
455         }
456         // Content
457
dialogArea = createDialogArea(composite);
458         // Create a grid data layout data if one was not provided.
459
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=118025
460
if (dialogArea.getLayoutData() == null) {
461             LAYOUTDATA_GRAB_BOTH.applyTo(dialogArea);
462         }
463         
464         // Info field
465
if (hasInfoArea()) {
466             infoSeparator = createHorizontalSeparator(composite);
467             createInfoTextArea(composite);
468         }
469
470         applyColors(composite);
471         applyFonts(composite);
472         return composite;
473     }
474
475     /**
476      * Creates and returns the contents of the dialog (the area below the title
477      * area and above the info text area.
478      * <p>
479      * The <code>PopupDialog</code> implementation of this framework method
480      * creates and returns a new <code>Composite</code> with standard margins
481      * and spacing.
482      * <p>
483      * The returned control's layout data must be an instance of
484      * <code>GridData</code>. This method must not modify the parent's
485      * layout.
486      * <p>
487      * Subclasses must override this method but may call <code>super</code> as
488      * in the following example:
489      *
490      * <pre>
491      * Composite composite = (Composite) super.createDialogArea(parent);
492      * //add controls to composite as necessary
493      * return composite;
494      * </pre>
495      *
496      * @param parent
497      * the parent composite to contain the dialog area
498      * @return the dialog area control
499      */

500     protected Control createDialogArea(Composite parent) {
501         Composite composite = new Composite(parent, SWT.NONE);
502         POPUP_LAYOUT_FACTORY.applyTo(composite);
503         LAYOUTDATA_GRAB_BOTH.applyTo(composite);
504         return composite;
505     }
506
507     /**
508      * Returns the control that should get initial focus. Subclasses may
509      * override this method.
510      *
511      * @return the Control that should receive focus when the popup opens.
512      */

513     protected Control getFocusControl() {
514         return dialogArea;
515     }
516
517     /**
518      * Sets the tab order for the popup. Clients should override to introduce
519      * specific tab ordering.
520      *
521      * @param composite
522      * the composite in which all content, including the title area
523      * and info area, was created. This composite's parent is the
524      * shell.
525      */

526     protected void setTabOrder(Composite composite) {
527         // default is to do nothing
528
}
529
530     /**
531      * Returns a boolean indicating whether the popup should have a title area
532      * at the top of the dialog. Subclasses may override. Default behavior is to
533      * have a title area if there is to be a menu or title text.
534      *
535      * @return <code>true</code> if a title area should be created,
536      * <code>false</code> if it should not.
537      */

538     protected boolean hasTitleArea() {
539         return titleText != null || showDialogMenu;
540     }
541
542     /**
543      * Returns a boolean indicating whether the popup should have an info area
544      * at the bottom of the dialog. Subclasses may override. Default behavior is
545      * to have an info area if info text was provided at the time of creation.
546      *
547      * @return <code>true</code> if a title area should be created,
548      * <code>false</code> if it should not.
549      */

550     protected boolean hasInfoArea() {
551         return infoText != null;
552     }
553
554     /**
555      * Creates the title and menu area. Subclasses typically need not override
556      * this method, but instead should use the constructor parameters
557      * <code>showDialogMenu</code> and <code>showPersistAction</code> to
558      * indicate whether a menu should be shown, and
559      * <code>createTitleControl</code> to to customize the presentation of the
560      * title.
561      *
562      * <p>
563      * If this method is overridden, the returned control's layout data must be
564      * an instance of <code>GridData</code>. This method must not modify the
565      * parent's layout.
566      *
567      * @param parent
568      * The parent composite.
569      * @return The Control representing the title and menu area.
570      */

571     protected Control createTitleMenuArea(Composite parent) {
572
573         Composite titleAreaComposite = new Composite(parent, SWT.NONE);
574         POPUP_LAYOUT_FACTORY.copy().numColumns(2).applyTo(titleAreaComposite);
575         GridDataFactory.fillDefaults()
576             .align(SWT.FILL, SWT.CENTER).grab(true, false)
577             .applyTo(titleAreaComposite);
578
579         createTitleControl(titleAreaComposite);
580
581         if (showDialogMenu) {
582             createDialogMenu(titleAreaComposite);
583         }
584         return titleAreaComposite;
585     }
586
587     /**
588      * Creates the control to be used to represent the dialog's title text.
589      * Subclasses may override if a different control is desired for
590      * representing the title text, or if something different than the title
591      * should be displayed in location where the title text typically is shown.
592      *
593      * <p>
594      * If this method is overridden, the returned control's layout data must be
595      * an instance of <code>GridData</code>. This method must not modify the
596      * parent's layout.
597      *
598      * @param parent
599      * The parent composite.
600      * @return The Control representing the title area.
601      */

602     protected Control createTitleControl(Composite parent) {
603         titleLabel = new Label(parent, SWT.NONE);
604
605         GridDataFactory.fillDefaults()
606             .align(SWT.FILL, SWT.CENTER)
607             .grab(true, false)
608             .span(showDialogMenu ? 1 : 2, 1)
609             .applyTo(titleLabel);
610         
611         Font font = titleLabel.getFont();
612         FontData[] fontDatas = font.getFontData();
613         for (int i = 0; i < fontDatas.length; i++) {
614             fontDatas[i].setStyle(SWT.BOLD);
615         }
616         titleFont = new Font(titleLabel.getDisplay(), fontDatas);
617         titleLabel.setFont(titleFont);
618         
619         if (titleText != null) {
620             titleLabel.setText(titleText);
621         }
622         return titleLabel;
623     }
624
625     /**
626      * Creates the optional info text area. This method is only called if the
627      * <code>hasInfoArea()</code> method returns true. Subclasses typically
628      * need not override this method, but may do so.
629      *
630      * <p>
631      * If this method is overridden, the returned control's layout data must be
632      * an instance of <code>GridData</code>. This method must not modify the
633      * parent's layout.
634      *
635      *
636      * @param parent
637      * The parent composite.
638      * @return The control representing the info text area.
639      *
640      * @see PopupDialog#hasInfoArea()
641      * @see PopupDialog#createTitleControl(Composite)
642      */

643     protected Control createInfoTextArea(Composite parent) {
644         // Status label
645
infoLabel = new Label(parent, SWT.RIGHT);
646         infoLabel.setText(infoText);
647         Font font = infoLabel.getFont();
648         FontData[] fontDatas = font.getFontData();
649         for (int i = 0; i < fontDatas.length; i++) {
650             fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
651         }
652         infoFont = new Font(infoLabel.getDisplay(), fontDatas);
653         infoLabel.setFont(infoFont);
654         GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING)
655             .applyTo(infoLabel);
656         infoLabel.setForeground(parent.getDisplay().getSystemColor(
657                 SWT.COLOR_WIDGET_DARK_SHADOW));
658         return infoLabel;
659     }
660
661     /**
662      * Create a horizontal separator for the given parent.
663      *
664      * @param parent
665      * The parent composite.
666      * @return The Control representing the horizontal separator.
667      */

668     private Control createHorizontalSeparator(Composite parent) {
669         Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL
670                 | SWT.LINE_DOT);
671         GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(separator);
672         return separator;
673     }
674
675     /**
676      * Create the dialog's menu for the move and resize actions.
677      *
678      * @param parent
679      * The parent composite.
680      */

681     private void createDialogMenu(Composite parent) {
682
683         toolBar = new ToolBar(parent, SWT.FLAT);
684         ToolItem viewMenuButton = new ToolItem(toolBar, SWT.PUSH, 0);
685
686         GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(toolBar);
687
688         menuImage = ImageDescriptor.createFromFile(PopupDialog.class,
689                 "images/popup_menu.gif").createImage();//$NON-NLS-1$
690
disabledMenuImage = ImageDescriptor.createFromFile(PopupDialog.class,
691                 "images/popup_menu_disabled.gif").createImage();//$NON-NLS-1$
692
viewMenuButton.setImage(menuImage);
693         viewMenuButton.setDisabledImage(disabledMenuImage);
694         viewMenuButton.setToolTipText(JFaceResources
695                 .getString("PopupDialog.menuTooltip")); //$NON-NLS-1$
696
viewMenuButton.addSelectionListener(new SelectionAdapter() {
697             public void widgetSelected(SelectionEvent e) {
698                 showDialogMenu();
699             }
700         });
701         viewMenuButton.addDisposeListener(new DisposeListener() {
702             public void widgetDisposed(DisposeEvent e) {
703                 menuImage.dispose();
704                 menuImage = null;
705                 disabledMenuImage.dispose();
706                 disabledMenuImage = null;
707             }
708         });
709         // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=177183
710
toolBar.addMouseListener(new MouseAdapter() {
711             public void mouseDown(MouseEvent e) {
712                 showDialogMenu();
713             }
714         });
715     }
716
717     /**
718      * Fill the dialog's menu. Subclasses may extend or override.
719      *
720      * @param dialogMenu
721      * The dialog's menu.
722      */

723     protected void fillDialogMenu(IMenuManager dialogMenu) {
724         dialogMenu.add(new GroupMarker("SystemMenuStart")); //$NON-NLS-1$
725
dialogMenu.add(new MoveAction());
726         dialogMenu.add(new ResizeAction());
727         if (showPersistAction) {
728             dialogMenu.add(new PersistBoundsAction());
729         }
730         dialogMenu.add(new Separator("SystemMenuEnd")); //$NON-NLS-1$
731
}
732
733     /**
734      * Perform the requested tracker action (resize or move).
735      *
736      * @param style
737      * The track style (resize or move).
738      */

739     private void performTrackerAction(int style) {
740         Shell shell = getShell();
741         if (shell == null || shell.isDisposed()) {
742             return;
743         }
744
745         Tracker tracker = new Tracker(shell.getDisplay(), style);
746         tracker.setStippled(true);
747         Rectangle[] r = new Rectangle[] { shell.getBounds() };
748         tracker.setRectangles(r);
749
750         // Ignore any deactivate events caused by opening the tracker.
751
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=120656
752
boolean oldListenToDeactivate = listenToDeactivate;
753         listenToDeactivate = false;
754         if (tracker.open()) {
755             if (shell != null && !shell.isDisposed()) {
756                 shell.setBounds(tracker.getRectangles()[0]);
757             }
758         }
759         listenToDeactivate = oldListenToDeactivate;
760     }
761
762     /**
763      * Show the dialog's menu. This message has no effect if the receiver was
764      * not configured to show a menu. Clients may call this method in order to
765      * trigger the menu via keystrokes or other gestures. Subclasses typically
766      * do not override method.
767      */

768     protected void showDialogMenu() {
769         if (!showDialogMenu) {
770             return;
771         }
772
773         if (menuManager == null) {
774             menuManager = new MenuManager();
775             fillDialogMenu(menuManager);
776         }
777         // Setting this flag works around a problem that remains on X only,
778
// whereby activating the menu deactivates our shell.
779
listenToDeactivate = !"gtk".equals(SWT.getPlatform()); //$NON-NLS-1$
780

781         Menu menu = menuManager.createContextMenu(getShell());
782         Rectangle bounds = toolBar.getBounds();
783         Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
784         topLeft = getShell().toDisplay(topLeft);
785         menu.setLocation(topLeft.x, topLeft.y);
786         menu.setVisible(true);
787     }
788
789     /**
790      * Set the text to be shown in the popup's info area. This message has no
791      * effect if there was no info text supplied when the dialog first opened.
792      * Subclasses may override this method.
793      *
794      * @param text
795      * the text to be shown when the info area is displayed.
796      *
797      */

798     protected void setInfoText(String JavaDoc text) {
799         infoText = text;
800         if (infoLabel != null) {
801             infoLabel.setText(text);
802         }
803     }
804
805     /**
806      * Set the text to be shown in the popup's title area. This message has no
807      * effect if there was no title label specified when the dialog was
808      * originally opened. Subclasses may override this method.
809      *
810      * @param text
811      * the text to be shown when the title area is displayed.
812      *
813      */

814     protected void setTitleText(String JavaDoc text) {
815         titleText = text;
816         if (titleLabel != null) {
817             titleLabel.setText(text);
818         }
819     }
820
821     /**
822      * Return a boolean indicating whether this dialog will persist its bounds.
823      * This value is initially set in the dialog's constructor, but can be
824      * modified if the persist bounds action is shown on the menu and the user
825      * has changed its value. Subclasses may override this method.
826      *
827      * @return <true> if the dialogs bounds will be persisted, false if it will
828      * not.
829      */

830     protected boolean getPersistBounds() {
831         return persistBounds;
832     }
833
834     /**
835      * Opens this window, creating it first if it has not yet been created.
836      * <p>
837      * This method is reimplemented for special configuration of PopupDialogs.
838      * It never blocks on open, immediately returning <code>OK</code> if the
839      * open is successful, or <code>CANCEL</code> if it is not. It provides
840      * framework hooks that allow subclasses to set the focus and tab order, and
841      * avoids the use of <code>shell.open()</code> in cases where the focus
842      * should not be given to the shell initially.
843      *
844      * @return the return code
845      *
846      * @see org.eclipse.jface.window.Window#open()
847      */

848     public int open() {
849
850         Shell shell = getShell();
851         if (shell == null || shell.isDisposed()) {
852             shell = null;
853             // create the window
854
create();
855             shell = getShell();
856         }
857
858         // provide a hook for adjusting the bounds. This is only
859
// necessary when there is content driven sizing that must be
860
// adjusted each time the dialog is opened.
861
adjustBounds();
862
863         // limit the shell size to the display size
864
constrainShellSize();
865
866         // set up the tab order for the dialog
867
setTabOrder((Composite) getContents());
868
869         // initialize flags for listening to deactivate
870
listenToDeactivate = false;
871         listenToParentDeactivate = false;
872
873         // open the window
874
if (takeFocusOnOpen) {
875             shell.open();
876             getFocusControl().setFocus();
877         } else {
878             shell.setVisible(true);
879         }
880
881         return OK;
882
883     }
884
885     /**
886      * Closes this window, disposes its shell, and removes this window from its
887      * window manager (if it has one).
888      * <p>
889      * This method is extended to save the dialog bounds and initialize widget
890      * state so that the widgets can be recreated if the dialog is reopened.
891      * This method may be extended (<code>super.close</code> must be called).
892      * </p>
893      *
894      * @return <code>true</code> if the window is (or was already) closed, and
895      * <code>false</code> if it is still open
896      */

897     public boolean close() {
898         // If already closed, there is nothing to do.
899
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127505
900
if (getShell() == null || getShell().isDisposed()) {
901             return true;
902         }
903         
904         saveDialogBounds(getShell());
905         // Widgets are about to be disposed, so null out any state
906
// related to them that was not handled in dispose listeners.
907
// We do this before disposal so that any received activate or
908
// deactivate events are duly ignored.
909
initializeWidgetState();
910         
911         if (parentDeactivateListener != null) {
912             getShell().getParent().removeListener(SWT.Deactivate, parentDeactivateListener);
913             parentDeactivateListener = null;
914         }
915
916         return super.close();
917     }
918
919     /**
920      * Gets the dialog settings that should be used for remembering the bounds
921      * of the dialog. Subclasses should override this method when they wish to
922      * persist the bounds of the dialog.
923      *
924      * @return settings the dialog settings used to store the dialog's location
925      * and/or size, or <code>null</code> if the dialog's bounds should
926      * never be stored.
927      */

928     protected IDialogSettings getDialogSettings() {
929         return null;
930     }
931
932     /**
933      * Saves the bounds of the shell in the appropriate dialog settings. The
934      * bounds are recorded relative to the parent shell, if there is one, or
935      * display coordinates if there is no parent shell. Subclasses typically
936      * need not override this method, but may extend it (calling
937      * <code>super.saveDialogBounds</code> if additional bounds information
938      * should be stored. Clients may also call this method to persist the bounds
939      * at times other than closing the dialog.
940      *
941      * @param shell
942      * The shell whose bounds are to be stored
943      */

944     protected void saveDialogBounds(Shell shell) {
945         IDialogSettings settings = getDialogSettings();
946         if (settings != null) {
947             Point shellLocation = shell.getLocation();
948             Point shellSize = shell.getSize();
949             Shell parent = getParentShell();
950             if (parent != null) {
951                 Point parentLocation = parent.getLocation();
952                 shellLocation.x -= parentLocation.x;
953                 shellLocation.y -= parentLocation.y;
954             }
955             if (persistBounds) {
956                 String JavaDoc prefix = getClass().getName();
957                 settings.put(prefix + DIALOG_ORIGIN_X, shellLocation.x);
958                 settings.put(prefix + DIALOG_ORIGIN_Y, shellLocation.y);
959                 settings.put(prefix + DIALOG_WIDTH, shellSize.x);
960                 settings.put(prefix + DIALOG_HEIGHT, shellSize.y);
961             }
962             if (showPersistAction && showDialogMenu) {
963                 settings.put(
964                         getClass().getName() + DIALOG_USE_PERSISTED_BOUNDS,
965                         persistBounds);
966             }
967         }
968     }
969
970     /*
971      * (non-Javadoc)
972      *
973      * @see org.eclipse.jface.window.Window#getInitialSize()
974      */

975     protected Point getInitialSize() {
976         Point result = super.getInitialSize();
977         if (persistBounds) {
978             IDialogSettings settings = getDialogSettings();
979             if (settings != null) {
980                 try {
981                     int width = settings.getInt(getClass().getName()
982                             + DIALOG_WIDTH);
983                     int height = settings.getInt(getClass().getName()
984                             + DIALOG_HEIGHT);
985                     result = new Point(width, height);
986
987                 } catch (NumberFormatException JavaDoc e) {
988                 }
989             }
990         }
991         // No attempt is made to constrain the bounds. The default
992
// constraining behavior in Window will be used.
993
return result;
994     }
995
996     /**
997      * Adjust the bounds of the popup as necessary prior to opening the dialog.
998      * Default is to do nothing, which honors any bounds set directly by clients
999      * or those that have been saved in the dialog settings. Subclasses should
1000     * override this method when there are bounds computations that must be
1001     * checked each time the dialog is opened.
1002     */

1003    protected void adjustBounds() {
1004    }
1005
1006    /**
1007     * (non-Javadoc)
1008     *
1009     * @see org.eclipse.jface.window.Window#getInitialLocation(org.eclipse.swt.graphics.Point)
1010     */

1011    protected Point getInitialLocation(Point initialSize) {
1012        Point result = super.getInitialLocation(initialSize);
1013        if (persistBounds) {
1014            IDialogSettings settings = getDialogSettings();
1015            if (settings != null) {
1016                try {
1017                    int x = settings.getInt(getClass().getName()
1018                            + DIALOG_ORIGIN_X);
1019                    int y = settings.getInt(getClass().getName()
1020                            + DIALOG_ORIGIN_Y);
1021                    result = new Point(x, y);
1022                    // The coordinates were stored relative to the parent shell.
1023
// Convert to display coordinates.
1024
Shell parent = getParentShell();
1025                    if (parent != null) {
1026                        Point parentLocation = parent.getLocation();
1027                        result.x += parentLocation.x;
1028                        result.y += parentLocation.y;
1029                    }
1030                } catch (NumberFormatException JavaDoc e) {
1031                }
1032            }
1033        }
1034        // No attempt is made to constrain the bounds. The default
1035
// constraining behavior in Window will be used.
1036
return result;
1037    }
1038
1039    /**
1040     * Apply any desired color to the specified composite and its children.
1041     *
1042     * @param composite
1043     * the contents composite
1044     */

1045    private void applyColors(Composite composite) {
1046        applyForegroundColor(getShell().getDisplay().getSystemColor(
1047                SWT.COLOR_INFO_FOREGROUND), composite,
1048                getForegroundColorExclusions());
1049        applyBackgroundColor(getShell().getDisplay().getSystemColor(
1050                SWT.COLOR_INFO_BACKGROUND), composite,
1051                getBackgroundColorExclusions());
1052    }
1053
1054    /**
1055     * Apply any desired fonts to the specified composite and its children.
1056     *
1057     * @param composite
1058     * the contents composite
1059     */

1060    private void applyFonts(Composite composite) {
1061        Dialog.applyDialogFont(composite);
1062
1063    }
1064
1065    /**
1066     * Set the specified foreground color for the specified control and all of
1067     * its children, except for those specified in the list of exclusions.
1068     *
1069     * @param color
1070     * the color to use as the foreground color
1071     * @param control
1072     * the control whose color is to be changed
1073     * @param exclusions
1074     * a list of controls who are to be excluded from getting their
1075     * color assigned
1076     */

1077    private void applyForegroundColor(Color color, Control control,
1078            List JavaDoc exclusions) {
1079        if (!exclusions.contains(control)) {
1080            control.setForeground(color);
1081        }
1082        if (control instanceof Composite) {
1083            Control[] children = ((Composite) control).getChildren();
1084            for (int i = 0; i < children.length; i++) {
1085                applyForegroundColor(color, children[i], exclusions);
1086            }
1087        }
1088    }
1089
1090    /**
1091     * Set the specified background color for the specified control and all of
1092     * its children.
1093     *
1094     * @param color
1095     * the color to use as the background color
1096     * @param control
1097     * the control whose color is to be changed
1098     * @param exclusions
1099     * a list of controls who are to be excluded from getting their
1100     * color assigned
1101     */

1102    private void applyBackgroundColor(Color color, Control control,
1103            List JavaDoc exclusions) {
1104        if (!exclusions.contains(control)) {
1105            control.setBackground(color);
1106        }
1107        if (control instanceof Composite) {
1108            Control[] children = ((Composite) control).getChildren();
1109            for (int i = 0; i < children.length; i++) {
1110                applyBackgroundColor(color, children[i], exclusions);
1111            }
1112        }
1113    }
1114
1115    /**
1116     * Set the specified foreground color for the specified control and all of
1117     * its children. Subclasses may override this method, but typically do not.
1118     * If a subclass wishes to exclude a particular control in its contents from
1119     * getting the specified foreground color, it may instead override
1120     * <code>PopupDialog.getForegroundColorExclusions</code>.
1121     *
1122     * @param color
1123     * the color to use as the background color
1124     * @param control
1125     * the control whose color is to be changed
1126     * @see PopupDialog#getBackgroundColorExclusions()
1127     */

1128    protected void applyForegroundColor(Color color, Control control) {
1129        applyForegroundColor(color, control, getForegroundColorExclusions());
1130    }
1131
1132    /**
1133     * Set the specified background color for the specified control and all of
1134     * its children. Subclasses may override this method, but typically do not.
1135     * If a subclass wishes to exclude a particular control in its contents from
1136     * getting the specified background color, it may instead override
1137     * <code>PopupDialog.getBackgroundColorExclusions</code>.
1138     *
1139     * @param color
1140     * the color to use as the background color
1141     * @param control
1142     * the control whose color is to be changed
1143     * @see PopupDialog#getBackgroundColorExclusions()
1144     */

1145    protected void applyBackgroundColor(Color color, Control control) {
1146        applyBackgroundColor(color, control, getBackgroundColorExclusions());
1147    }
1148
1149    /**
1150     * Return a list of controls which should never have their foreground color
1151     * reset. Subclasses may extend this method (should always call
1152     * <code>super.getForegroundColorExclusions</code> to aggregate the list.
1153     *
1154     *
1155     * @return the List of controls
1156     */

1157    protected List JavaDoc getForegroundColorExclusions() {
1158        List JavaDoc list = new ArrayList JavaDoc(3);
1159        if (infoLabel != null) {
1160            list.add(infoLabel);
1161        }
1162        if (titleSeparator != null) {
1163            list.add(titleSeparator);
1164        }
1165        if (infoSeparator != null) {
1166            list.add(infoSeparator);
1167        }
1168        return list;
1169    }
1170
1171    /**
1172     * Return a list of controls which should never have their background color
1173     * reset. Subclasses may extend this method (should always call
1174     * <code>super.getBackgroundColorExclusions</code> to aggregate the list.
1175     *
1176     * @return the List of controls
1177     */

1178    protected List JavaDoc getBackgroundColorExclusions() {
1179        List JavaDoc list = new ArrayList JavaDoc(2);
1180        if (titleSeparator != null) {
1181            list.add(titleSeparator);
1182        }
1183        if (infoSeparator != null) {
1184            list.add(infoSeparator);
1185        }
1186        return list;
1187    }
1188
1189    /**
1190     * Initialize any state related to the widgetry that should be set up each
1191     * time widgets are created.
1192     */

1193    private void initializeWidgetState() {
1194        menuManager = null;
1195        dialogArea = null;
1196        titleLabel = null;
1197        titleSeparator = null;
1198        infoSeparator = null;
1199        infoLabel = null;
1200        toolBar = null;
1201
1202        // If the menu item for persisting bounds is displayed, use the stored
1203
// value to determine whether any persisted bounds should be honored at
1204
// all.
1205
if (showDialogMenu && showPersistAction) {
1206            IDialogSettings settings = getDialogSettings();
1207            if (settings != null) {
1208                persistBounds = settings.getBoolean(getClass().getName()
1209                        + DIALOG_USE_PERSISTED_BOUNDS);
1210            }
1211        }
1212
1213    }
1214    
1215    /**
1216     * The dialog is being disposed. Dispose of any resources allocated.
1217     *
1218     */

1219    private void handleDispose() {
1220        if (infoFont != null && !infoFont.isDisposed()) {
1221            infoFont.dispose();
1222        }
1223        infoFont = null;
1224        if (titleFont != null && !titleFont.isDisposed()) {
1225            titleFont.dispose();
1226        }
1227        titleFont = null;
1228    }
1229}
1230
Popular Tags