1 /******************************************************************************* 2 * Copyright (c) 2005, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Stefan Xenos, IBM - bug 156790: Adopt GridLayoutFactory within JFace 14 *******************************************************************************/ 15 package org.eclipse.jface.dialogs; 16 17 import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; 18 19 import java.util.ArrayList; 20 import java.util.List; 21 22 import org.eclipse.swt.SWT; 23 import org.eclipse.swt.events.MouseAdapter; 24 import org.eclipse.swt.events.MouseEvent; 25 import org.eclipse.swt.graphics.Color; 26 import org.eclipse.swt.graphics.Font; 27 import org.eclipse.swt.graphics.FontData; 28 import org.eclipse.swt.graphics.Point; 29 import org.eclipse.swt.graphics.RGB; 30 import org.eclipse.swt.graphics.Rectangle; 31 import org.eclipse.swt.widgets.Composite; 32 import org.eclipse.swt.widgets.Control; 33 import org.eclipse.swt.widgets.Display; 34 import org.eclipse.swt.widgets.Event; 35 import org.eclipse.swt.widgets.Label; 36 import org.eclipse.swt.widgets.Listener; 37 import org.eclipse.swt.widgets.Menu; 38 import org.eclipse.swt.widgets.Shell; 39 import org.eclipse.swt.widgets.ToolBar; 40 import org.eclipse.swt.widgets.ToolItem; 41 import org.eclipse.swt.widgets.Tracker; 42 43 import org.eclipse.core.runtime.Assert; 44 45 import org.eclipse.jface.action.Action; 46 import org.eclipse.jface.action.GroupMarker; 47 import org.eclipse.jface.action.IAction; 48 import org.eclipse.jface.action.IMenuManager; 49 import org.eclipse.jface.action.MenuManager; 50 import org.eclipse.jface.action.Separator; 51 import org.eclipse.jface.layout.GridDataFactory; 52 import org.eclipse.jface.layout.GridLayoutFactory; 53 import org.eclipse.jface.preference.JFacePreferences; 54 import org.eclipse.jface.resource.JFaceResources; 55 import org.eclipse.jface.util.Util; 56 import org.eclipse.jface.window.Window; 57 58 /** 59 * A lightweight, transient dialog that is popped up to show contextual or 60 * temporal information and is easily dismissed. Clients control whether the 61 * dialog should be able to receive input focus. An optional title area at the 62 * top and an optional info area at the bottom can be used to provide additional 63 * information. 64 * <p> 65 * Because the dialog is short-lived, most of the configuration of the dialog is 66 * done in the constructor. Set methods are only provided for those values that 67 * are expected to be dynamically computed based on a particular instance's 68 * internal state. 69 * <p> 70 * Clients are expected to override the creation of the main dialog area, and 71 * may optionally override the creation of the title area and info area in order 72 * to add content. In general, however, the creation of stylistic features, such 73 * as the dialog menu, separator styles, and fonts, is kept private so that all 74 * popup dialogs will have a similar appearance. 75 * 76 * @since 3.2 77 */ 78 public class PopupDialog extends Window { 79 80 private static GridDataFactory grabBothGridDataFactory; 81 getGrabBothGridData()82 private static GridDataFactory getGrabBothGridData() { 83 if (grabBothGridDataFactory == null) { 84 grabBothGridDataFactory = GridDataFactory.fillDefaults().grab(true, true); 85 } 86 return grabBothGridDataFactory; 87 } 88 89 /** 90 * The dialog settings key name for stored dialog x location. 91 */ 92 private static final String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$ 93 94 /** 95 * The dialog settings key name for stored dialog y location. 96 */ 97 private static final String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$ 98 99 /** 100 * The dialog settings key name for stored dialog width. 101 */ 102 private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$ 103 104 /** 105 * The dialog settings key name for stored dialog height. 106 */ 107 private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$ 108 109 /** 110 * The dialog settings key name for remembering if the persisted size should 111 * be accessed. 112 */ 113 private static final String DIALOG_USE_PERSISTED_SIZE = "DIALOG_USE_PERSISTED_SIZE"; //$NON-NLS-1$ 114 115 /** 116 * The dialog settings key name for remembering if the persisted location 117 * should be accessed. 118 */ 119 private static final String DIALOG_USE_PERSISTED_LOCATION = "DIALOG_USE_PERSISTED_LOCATION"; //$NON-NLS-1$ 120 121 /** 122 * Move action for the dialog. 123 */ 124 private class MoveAction extends Action { 125 MoveAction()126 MoveAction() { 127 super(JFaceResources.getString("PopupDialog.move"), //$NON-NLS-1$ 128 IAction.AS_PUSH_BUTTON); 129 } 130 131 @Override run()132 public void run() { 133 performTrackerAction(SWT.NONE); 134 } 135 136 } 137 138 /** 139 * Resize action for the dialog. 140 */ 141 private class ResizeAction extends Action { 142 ResizeAction()143 ResizeAction() { 144 super(JFaceResources.getString("PopupDialog.resize"), IAction.AS_PUSH_BUTTON); //$NON-NLS-1$ 145 } 146 147 @Override run()148 public void run() { 149 performTrackerAction(SWT.RESIZE); 150 } 151 } 152 153 /** 154 * 155 * Remember bounds action for the dialog. 156 */ 157 private class PersistBoundsAction extends Action { 158 PersistBoundsAction()159 PersistBoundsAction() { 160 super(JFaceResources.getString("PopupDialog.persistBounds"), IAction.AS_CHECK_BOX); //$NON-NLS-1$ 161 setChecked(persistLocation && persistSize); 162 } 163 164 @Override run()165 public void run() { 166 persistSize = isChecked(); 167 persistLocation = persistSize; 168 } 169 } 170 171 /** 172 * 173 * Remember bounds action for the dialog. 174 */ 175 private class PersistSizeAction extends Action { 176 PersistSizeAction()177 PersistSizeAction() { 178 super(JFaceResources.getString("PopupDialog.persistSize"), IAction.AS_CHECK_BOX); //$NON-NLS-1$ 179 setChecked(persistSize); 180 } 181 182 @Override run()183 public void run() { 184 persistSize = isChecked(); 185 } 186 } 187 188 /** 189 * 190 * Remember location action for the dialog. 191 */ 192 private class PersistLocationAction extends Action { 193 PersistLocationAction()194 PersistLocationAction() { 195 super(JFaceResources.getString("PopupDialog.persistLocation"), IAction.AS_CHECK_BOX); //$NON-NLS-1$ 196 setChecked(persistLocation); 197 } 198 199 @Override run()200 public void run() { 201 persistLocation = isChecked(); 202 } 203 } 204 205 /** 206 * Shell style appropriate for a simple hover popup that cannot get focus. 207 * 208 */ 209 public static final int HOVER_SHELLSTYLE = SWT.NO_FOCUS | SWT.ON_TOP | SWT.TOOL; 210 211 /** 212 * Shell style appropriate for an info popup that can get focus. 213 */ 214 public static final int INFOPOPUP_SHELLSTYLE = SWT.TOOL; 215 216 /** 217 * Shell style appropriate for a resizable info popup that can get focus. 218 */ 219 public static final int INFOPOPUPRESIZE_SHELLSTYLE = SWT.RESIZE; 220 221 /** 222 * Margin width (in pixels) to be used in layouts inside popup dialogs 223 * (value is 0). 224 */ 225 public static final int POPUP_MARGINWIDTH = 0; 226 227 /** 228 * Margin height (in pixels) to be used in layouts inside popup dialogs 229 * (value is 0). 230 */ 231 public static final int POPUP_MARGINHEIGHT = 0; 232 233 /** 234 * Vertical spacing (in pixels) between cells in the layouts inside popup 235 * dialogs (value is 1). 236 */ 237 public static final int POPUP_VERTICALSPACING = 1; 238 239 /** 240 * Vertical spacing (in pixels) between cells in the layouts inside popup 241 * dialogs (value is 1). 242 */ 243 public static final int POPUP_HORIZONTALSPACING = 1; 244 245 /** 246 * Image registry key for menu image. 247 * 248 * @since 3.4 249 */ 250 public static final String POPUP_IMG_MENU = "popup_menu_image"; //$NON-NLS-1$ 251 252 /** 253 * Image registry key for disabled menu image. 254 * 255 * @since 3.4 256 */ 257 public static final String POPUP_IMG_MENU_DISABLED = "popup_menu_image_diabled"; //$NON-NLS-1$ 258 259 /** 260 * 261 */ 262 private static GridLayoutFactory popupLayoutFactory; getPopupLayout()263 private static GridLayoutFactory getPopupLayout() { 264 if (popupLayoutFactory == null) { 265 popupLayoutFactory = GridLayoutFactory.fillDefaults() 266 .margins(POPUP_MARGINWIDTH, POPUP_MARGINHEIGHT) 267 .spacing(POPUP_HORIZONTALSPACING, POPUP_VERTICALSPACING); 268 } 269 return popupLayoutFactory; 270 } 271 272 /** 273 * The dialog's toolbar for the move and resize capabilities. 274 */ 275 private ToolBar toolBar = null; 276 277 /** 278 * The dialog's menu manager. 279 */ 280 private MenuManager menuManager = null; 281 282 /** 283 * The control representing the main dialog area. 284 */ 285 private Control dialogArea; 286 287 /** 288 * Labels that contain title and info text. Cached so they can be updated 289 * dynamically if possible. 290 */ 291 private Label titleLabel, infoLabel; 292 293 /** 294 * Separator controls. Cached so they can be excluded from color changes. 295 */ 296 private Control titleSeparator, infoSeparator; 297 298 /** 299 * Color to be used for the info area text. 300 * 301 * @since 3.6 302 */ 303 private Color infoColor; 304 305 /** 306 * Font to be used for the info area text. Computed based on the dialog's 307 * font. 308 */ 309 private Font infoFont; 310 311 /** 312 * Font to be used for the title area text. Computed based on the dialog's 313 * font. 314 */ 315 private Font titleFont; 316 317 /** 318 * Flags indicating whether we are listening for shell deactivate events, 319 * either those or our parent's. Used to prevent closure when a menu command 320 * is chosen or a secondary popup is launched. 321 */ 322 private boolean listenToDeactivate; 323 324 private boolean listenToParentDeactivate; 325 326 private Listener parentDeactivateListener; 327 328 /** 329 * Flag indicating whether focus should be taken when the dialog is opened. 330 */ 331 private boolean takeFocusOnOpen = false; 332 333 /** 334 * Flag specifying whether a menu should be shown that allows the user to 335 * move and resize. 336 */ 337 private boolean showDialogMenu = false; 338 339 /** 340 * Flag specifying whether menu actions allowing the user to choose whether 341 * the dialog bounds and location should be persisted are to be shown. 342 */ 343 private boolean showPersistActions = false; 344 345 /** 346 * Flag specifying whether the size of the popup should be persisted. This 347 * flag is used as initial default and updated by the menu if it is shown. 348 */ 349 private boolean persistSize = false; 350 351 /** 352 * Flag specifying whether the location of the popup should be persisted. 353 * This flag is used as initial default and updated by the menu if it is 354 * shown. 355 */ 356 private boolean persistLocation = false; 357 /** 358 * Flag specifying whether to use new 3.4 API instead of the old one. 359 * 360 * @since 3.4 361 */ 362 private boolean isUsing34API = true; 363 364 /** 365 * Text to be shown in an optional title area (on top). 366 */ 367 private String titleText; 368 369 /** 370 * Text to be shown in an optional info area (at the bottom). 371 */ 372 private String infoText; 373 374 /** 375 * Constructs a new instance of <code>PopupDialog</code>. 376 * 377 * @param parent 378 * The parent shell. 379 * @param shellStyle 380 * The shell style. 381 * @param takeFocusOnOpen 382 * A boolean indicating whether focus should be 383 * taken by this popup when it opens. 384 * @param persistBounds 385 * A boolean indicating whether the bounds (size 386 * and location) of the dialog should be persisted 387 * upon close of the dialog. The bounds can only 388 * be persisted if the dialog settings for 389 * persisting the bounds are also specified. If a 390 * menu action will be provided that allows the 391 * user to control this feature, then the last 392 * known value of the user's setting will be used 393 * instead of this flag. 394 * @param showDialogMenu 395 * A boolean indicating whether a menu for moving 396 * and resizing the popup should be provided. 397 * @param showPersistActions 398 * A boolean indicating whether actions allowing 399 * the user to control the persisting of the 400 * dialog size and location should be shown in the 401 * dialog menu. This parameter has no effect if 402 * <code>showDialogMenu</code> is 403 * <code>false</code>. 404 * @param titleText 405 * Text to be shown in an upper title area, or 406 * <code>null</code> if there is no title. 407 * @param infoText 408 * Text to be shown in a lower info area, or 409 * <code>null</code> if there is no info area. 410 * 411 * @see PopupDialog#getDialogSettings() 412 * @deprecated As of 3.4, replaced by 413 * {@link #PopupDialog(Shell, int, boolean, boolean, boolean, boolean, boolean, String, String)} 414 * 415 * @noreference Planned for deletion see 416 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=531913 417 */ 418 @Deprecated PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen, boolean persistBounds, boolean showDialogMenu, boolean showPersistActions, String titleText, String infoText)419 public PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen, 420 boolean persistBounds, boolean showDialogMenu, 421 boolean showPersistActions, String titleText, String infoText) { 422 this(parent, shellStyle, takeFocusOnOpen, persistBounds, persistBounds, 423 showDialogMenu, showPersistActions, titleText, infoText, false); 424 } 425 426 /** 427 * Constructs a new instance of <code>PopupDialog</code>. 428 * 429 * @param parent 430 * The parent shell. 431 * @param shellStyle 432 * The shell style. 433 * @param takeFocusOnOpen 434 * A boolean indicating whether focus should be taken by this 435 * popup when it opens. 436 * @param persistSize 437 * A boolean indicating whether the size should be persisted upon 438 * close of the dialog. The size can only be persisted if the 439 * dialog settings for persisting the bounds are also specified. 440 * If a menu action will be provided that allows the user to 441 * control this feature and the user hasn't changed that setting, 442 * then this flag is used as initial default for the menu. 443 * @param persistLocation 444 * A boolean indicating whether the location should be persisted 445 * upon close of the dialog. The location can only be persisted 446 * if the dialog settings for persisting the bounds are also 447 * specified. If a menu action will be provided that allows the 448 * user to control this feature and the user hasn't changed that 449 * setting, then this flag is used as initial default for the 450 * menu. default for the menu until the user changed it. 451 * @param showDialogMenu 452 * A boolean indicating whether a menu for moving and resizing 453 * the popup should be provided. 454 * @param showPersistActions 455 * A boolean indicating whether actions allowing the user to 456 * control the persisting of the dialog bounds and location 457 * should be shown in the dialog menu. This parameter has no 458 * effect if <code>showDialogMenu</code> is <code>false</code>. 459 * @param titleText 460 * Text to be shown in an upper title area, or <code>null</code> 461 * if there is no title. 462 * @param infoText 463 * Text to be shown in a lower info area, or <code>null</code> 464 * if there is no info area. 465 * 466 * @see PopupDialog#getDialogSettings() 467 * 468 * @since 3.4 469 */ PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen, boolean persistSize, boolean persistLocation, boolean showDialogMenu, boolean showPersistActions, String titleText, String infoText)470 public PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen, 471 boolean persistSize, boolean persistLocation, 472 boolean showDialogMenu, boolean showPersistActions, 473 String titleText, String infoText) { 474 this(parent, shellStyle, takeFocusOnOpen, persistSize, persistLocation, 475 showDialogMenu, showPersistActions, titleText, infoText, true); 476 477 } 478 479 /** 480 * Constructs a new instance of <code>PopupDialog</code>. 481 * 482 * @param parent 483 * The parent shell. 484 * @param shellStyle 485 * The shell style. 486 * @param takeFocusOnOpen 487 * A boolean indicating whether focus should be taken by this 488 * popup when it opens. 489 * @param persistSize 490 * A boolean indicating whether the size should be persisted upon 491 * close of the dialog. The size can only be persisted if the 492 * dialog settings for persisting the bounds are also specified. 493 * If a menu action will be provided that allows the user to 494 * control this feature and the user hasn't changed that setting, 495 * then this flag is used as initial default for the menu. 496 * @param persistLocation 497 * A boolean indicating whether the location should be persisted 498 * upon close of the dialog. The location can only be persisted 499 * if the dialog settings for persisting the bounds are also 500 * specified. If a menu action will be provided that allows the 501 * user to control this feature and the user hasn't changed that 502 * setting, then this flag is used as initial default for the 503 * menu. default for the menu until the user changed it. 504 * @param showDialogMenu 505 * A boolean indicating whether a menu for moving and resizing 506 * the popup should be provided. 507 * @param showPersistActions 508 * A boolean indicating whether actions allowing the user to 509 * control the persisting of the dialog bounds and location 510 * should be shown in the dialog menu. This parameter has no 511 * effect if <code>showDialogMenu</code> is <code>false</code>. 512 * @param titleText 513 * Text to be shown in an upper title area, or <code>null</code> 514 * if there is no title. 515 * @param infoText 516 * Text to be shown in a lower info area, or <code>null</code> 517 * if there is no info area. 518 * @param use34API 519 * <code>true</code> if 3.4 API should be used 520 * 521 * @see PopupDialog#getDialogSettings() 522 * 523 * @since 3.4 524 */ PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen, boolean persistSize, boolean persistLocation, boolean showDialogMenu, boolean showPersistActions, String titleText, String infoText, boolean use34API)525 private PopupDialog(Shell parent, int shellStyle, boolean takeFocusOnOpen, 526 boolean persistSize, boolean persistLocation, 527 boolean showDialogMenu, boolean showPersistActions, 528 String titleText, String infoText, boolean use34API) { 529 super(parent); 530 // Prior to 3.4, we encouraged use of SWT.NO_TRIM and provided a 531 // border using a black composite background and margin. Now we 532 // use SWT.TOOL to get the border for some cases and this conflicts 533 // with SWT.NO_TRIM. Clients who previously have used SWT.NO_TRIM 534 // and still had a border drawn for them would find their border go 535 // away unless we do the following: 536 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=219743 537 if ((shellStyle & SWT.NO_TRIM) != 0) { 538 shellStyle &= ~(SWT.NO_TRIM | SWT.SHELL_TRIM); 539 } 540 541 setShellStyle(shellStyle); 542 this.takeFocusOnOpen = takeFocusOnOpen; 543 this.showDialogMenu = showDialogMenu; 544 this.showPersistActions = showPersistActions; 545 this.titleText = titleText; 546 this.infoText = infoText; 547 548 setBlockOnOpen(false); 549 550 this.isUsing34API = use34API; 551 552 this.persistSize = persistSize; 553 this.persistLocation = persistLocation; 554 555 initializeWidgetState(); 556 } 557 558 @Override configureShell(Shell shell)559 protected void configureShell(Shell shell) { 560 GridLayoutFactory.fillDefaults().margins(0, 0).spacing(5, 5).applyTo( 561 shell); 562 shell.addListener(SWT.Deactivate, event -> { 563 /* 564 * Close if we are deactivating and have no child shells. If we 565 * have child shells, we are deactivating due to their opening. 566 * 567 * Feature in GTK: this causes the Quick Outline/Type Hierarchy 568 * Shell to close on re-size/movement on Gtk3. For this reason, 569 * the asyncClose() call is disabled in GTK. See Eclipse Bugs 570 * 466500 and 113577 for more information. 571 */ 572 if (listenToDeactivate && event.widget == getShell() 573 && getShell().getShells().length == 0) { 574 if (!Util.isGtk()) { 575 asyncClose(); 576 } 577 } else { 578 /* 579 * We typically ignore deactivates to work around 580 * platform-specific event ordering. Now that we've ignored 581 * whatever we were supposed to, start listening to 582 * deactivates. Example issues can be found in 583 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=123392 584 */ 585 listenToDeactivate = true; 586 } 587 }); 588 // Set this true whenever we activate. It may have been turned 589 // off by a menu or secondary popup showing. 590 shell.addListener(SWT.Activate, event -> { 591 // ignore this event if we have launched a child 592 if (event.widget == getShell() && getShell().getShells().length == 0) { 593 listenToDeactivate = true; 594 // Typically we start listening for parent deactivate after 595 // we are activated, except on the Mac, where the deactivate 596 // is received after activate. 597 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=100668 598 listenToParentDeactivate = !Util.isMac(); 599 } 600 }); 601 602 final Composite parent = shell.getParent(); 603 if (parent != null) { 604 if ((getShellStyle() & SWT.ON_TOP) != 0) { 605 parentDeactivateListener = event -> { 606 if (listenToParentDeactivate) { 607 asyncClose(); 608 } else { 609 // Our first deactivate, now start listening on the Mac. 610 listenToParentDeactivate = listenToDeactivate; 611 } 612 }; 613 parent.addListener(SWT.Deactivate, parentDeactivateListener); 614 } else if (Util.isGtk()) { 615 /* 616 * Fix for bug 485745 on GTK: popup does not close on parent 617 * shell activation. 618 */ 619 parent.addListener(SWT.Activate, new Listener() { 620 @Override 621 public void handleEvent(Event event) { 622 /* 623 * NB: we must wait with closing until 624 * listenToDeactivate is set to true, otherwise it may 625 * happen that the popup closes immediately after 626 * showing up (seem to be timing issue with shell 627 * creation). 628 * 629 * E.g. "Display" popup does not need this, but 630 * "Show all Instances" and "Show all References" do. 631 * They all are InspectPopupDialog instances... 632 */ 633 if (event.widget != parent || !listenToDeactivate || parent.isDisposed()) { 634 return; 635 } 636 parent.removeListener(SWT.Activate, this); 637 asyncClose(); 638 } 639 }); 640 } 641 } 642 643 shell.addDisposeListener(event -> handleDispose()); 644 } 645 asyncClose()646 private void asyncClose() { 647 // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=152010 648 Shell shell = getShell(); 649 if (shell != null && !shell.isDisposed()) { 650 shell.getDisplay().asyncExec(() -> close()); 651 } 652 } 653 654 /** 655 * The <code>PopupDialog</code> implementation of this <code>Window</code> 656 * method creates and lays out the top level composite for the dialog. It 657 * then calls the <code>createTitleMenuArea</code>, 658 * <code>createDialogArea</code>, and <code>createInfoTextArea</code> 659 * methods to create an optional title and menu area on the top, a dialog 660 * area in the center, and an optional info text area at the bottom. 661 * Overriding <code>createDialogArea</code> and (optionally) 662 * <code>createTitleMenuArea</code> and <code>createTitleMenuArea</code> 663 * are recommended rather than overriding this method. 664 * 665 * @param parent 666 * the composite used to parent the contents. 667 * 668 * @return the control representing the contents. 669 */ 670 @Override createContents(Composite parent)671 protected Control createContents(Composite parent) { 672 Composite composite = new Composite(parent, SWT.NONE); 673 getPopupLayout().applyTo(composite); 674 getGrabBothGridData().applyTo(composite); 675 676 // Title area 677 if (hasTitleArea()) { 678 createTitleMenuArea(composite); 679 titleSeparator = createHorizontalSeparator(composite); 680 } 681 // Content 682 dialogArea = createDialogArea(composite); 683 // Create a grid data layout data if one was not provided. 684 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=118025 685 if (dialogArea.getLayoutData() == null) { 686 getGrabBothGridData().applyTo(dialogArea); 687 } 688 689 // Info field 690 if (hasInfoArea()) { 691 infoSeparator = createHorizontalSeparator(composite); 692 createInfoTextArea(composite); 693 } 694 695 applyColors(composite); 696 applyFonts(composite); 697 return composite; 698 } 699 700 /** 701 * Creates and returns the contents of the dialog (the area below the title 702 * area and above the info text area. 703 * <p> 704 * The <code>PopupDialog</code> implementation of this framework method 705 * creates and returns a new <code>Composite</code> with standard margins 706 * and spacing. 707 * <p> 708 * The returned control's layout data must be an instance of 709 * <code>GridData</code>. This method must not modify the parent's 710 * layout. 711 * <p> 712 * Subclasses must override this method but may call <code>super</code> as 713 * in the following example: 714 * 715 * <pre> 716 * Composite composite = (Composite) super.createDialogArea(parent); 717 * //add controls to composite as necessary 718 * return composite; 719 * </pre> 720 * 721 * @param parent 722 * the parent composite to contain the dialog area 723 * @return the dialog area control 724 */ createDialogArea(Composite parent)725 protected Control createDialogArea(Composite parent) { 726 Composite composite = new Composite(parent, SWT.NONE); 727 getPopupLayout().applyTo(composite); 728 getGrabBothGridData().applyTo(composite); 729 return composite; 730 } 731 732 /** 733 * Returns the control that should get initial focus. Subclasses may 734 * override this method. 735 * 736 * @return the Control that should receive focus when the popup opens. 737 */ getFocusControl()738 protected Control getFocusControl() { 739 return dialogArea; 740 } 741 742 /** 743 * Sets the tab order for the popup. Clients should override to introduce 744 * specific tab ordering. 745 * 746 * @param composite 747 * the composite in which all content, including the title area 748 * and info area, was created. This composite's parent is the 749 * shell. 750 */ setTabOrder(Composite composite)751 protected void setTabOrder(Composite composite) { 752 // default is to do nothing 753 } 754 755 /** 756 * Returns a boolean indicating whether the popup should have a title area 757 * at the top of the dialog. Subclasses may override. Default behavior is to 758 * have a title area if there is to be a menu or title text. 759 * 760 * @return <code>true</code> if a title area should be created, 761 * <code>false</code> if it should not. 762 */ hasTitleArea()763 protected boolean hasTitleArea() { 764 return titleText != null || showDialogMenu; 765 } 766 767 /** 768 * Returns a boolean indicating whether the popup should have an info area 769 * at the bottom of the dialog. Subclasses may override. Default behavior is 770 * to have an info area if info text was provided at the time of creation. 771 * 772 * @return <code>true</code> if a title area should be created, 773 * <code>false</code> if it should not. 774 */ hasInfoArea()775 protected boolean hasInfoArea() { 776 return infoText != null; 777 } 778 779 /** 780 * Creates the title and menu area. Subclasses typically need not override 781 * this method, but instead should use the constructor parameters 782 * <code>showDialogMenu</code> and <code>showPersistAction</code> to 783 * indicate whether a menu should be shown, and 784 * <code>createTitleControl</code> to to customize the presentation of the 785 * title. 786 * 787 * <p> 788 * If this method is overridden, the returned control's layout data must be 789 * an instance of <code>GridData</code>. This method must not modify the 790 * parent's layout. 791 * 792 * @param parent 793 * The parent composite. 794 * @return The Control representing the title and menu area. 795 */ createTitleMenuArea(Composite parent)796 protected Control createTitleMenuArea(Composite parent) { 797 798 Composite titleAreaComposite = new Composite(parent, SWT.NONE); 799 getPopupLayout().copy().numColumns(2).applyTo(titleAreaComposite); 800 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(titleAreaComposite); 801 802 createTitleControl(titleAreaComposite); 803 804 if (showDialogMenu) { 805 createDialogMenu(titleAreaComposite); 806 } 807 return titleAreaComposite; 808 } 809 810 /** 811 * Creates the control to be used to represent the dialog's title text. 812 * Subclasses may override if a different control is desired for 813 * representing the title text, or if something different than the title 814 * should be displayed in location where the title text typically is shown. 815 * 816 * <p> 817 * If this method is overridden, the returned control's layout data must be 818 * an instance of <code>GridData</code>. This method must not modify the 819 * parent's layout. 820 * 821 * @param parent 822 * The parent composite. 823 * @return The Control representing the title area. 824 */ createTitleControl(Composite parent)825 protected Control createTitleControl(Composite parent) { 826 titleLabel = new Label(parent, SWT.NONE); 827 828 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, 829 false).span(showDialogMenu ? 1 : 2, 1).applyTo(titleLabel); 830 831 if (titleText != null) { 832 titleLabel.setText(titleText); 833 } 834 return titleLabel; 835 } 836 837 /** 838 * Creates the optional info text area. This method is only called if the 839 * <code>hasInfoArea()</code> method returns true. Subclasses typically 840 * need not override this method, but may do so. 841 * 842 * <p> 843 * If this method is overridden, the returned control's layout data must be 844 * an instance of <code>GridData</code>. This method must not modify the 845 * parent's layout. 846 * 847 * 848 * @param parent 849 * The parent composite. 850 * @return The control representing the info text area. 851 * 852 * @see PopupDialog#hasInfoArea() 853 * @see PopupDialog#createTitleControl(Composite) 854 */ createInfoTextArea(Composite parent)855 protected Control createInfoTextArea(Composite parent) { 856 // Status label 857 infoLabel = new Label(parent, SWT.RIGHT); 858 infoLabel.setText(infoText); 859 860 GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, 861 SWT.BEGINNING).applyTo(infoLabel); 862 Display display = parent.getDisplay(); 863 864 Color backgroundColor = getBackground(); 865 if (backgroundColor == null) 866 backgroundColor = getDefaultBackground(); 867 Color foregroundColor = getForeground(); 868 if (foregroundColor == null) 869 foregroundColor = getDefaultForeground(); 870 infoColor = new Color(display, blend( 871 backgroundColor.getRGB(), foregroundColor.getRGB(), 872 0.56f)); 873 874 infoLabel.setForeground(infoColor); 875 return infoLabel; 876 } 877 878 /** 879 * Returns an RGB that lies between the given foreground and background 880 * colors using the given mixing factor. A <code>factor</code> of 1.0 will produce a 881 * color equal to <code>fg</code>, while a <code>factor</code> of 0.0 will produce one 882 * equal to <code>bg</code>. 883 * @param bg the background color 884 * @param fg the foreground color 885 * @param factor the mixing factor, must be in [0, 1] 886 * 887 * @return the interpolated color 888 * @since 3.6 889 */ blend(RGB bg, RGB fg, float factor)890 private static RGB blend(RGB bg, RGB fg, float factor) { 891 // copy of org.eclipse.jface.internal.text.revisions.Colors#blend(..) 892 Assert.isLegal(bg != null); 893 Assert.isLegal(fg != null); 894 Assert.isLegal(factor >= 0f && factor <= 1f); 895 896 float complement = 1f - factor; 897 return new RGB( 898 (int) (complement * bg.red + factor * fg.red), 899 (int) (complement * bg.green + factor * fg.green), 900 (int) (complement * bg.blue + factor * fg.blue) 901 ); 902 } 903 904 /** 905 * Create a horizontal separator for the given parent. 906 * 907 * @param parent 908 * The parent composite. 909 * @return The Control representing the horizontal separator. 910 */ createHorizontalSeparator(Composite parent)911 private Control createHorizontalSeparator(Composite parent) { 912 Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); 913 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, 914 false).applyTo(separator); 915 return separator; 916 } 917 918 /** 919 * Create the dialog's menu for the move and resize actions. 920 * 921 * @param parent 922 * The parent composite. 923 */ createDialogMenu(Composite parent)924 private void createDialogMenu(Composite parent) { 925 926 toolBar = new ToolBar(parent, SWT.FLAT); 927 ToolItem viewMenuButton = new ToolItem(toolBar, SWT.PUSH, 0); 928 929 GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(toolBar); 930 viewMenuButton.setImage(JFaceResources.getImage(POPUP_IMG_MENU)); 931 viewMenuButton.setDisabledImage(JFaceResources.getImage(POPUP_IMG_MENU_DISABLED)); 932 viewMenuButton.setToolTipText(JFaceResources.getString("PopupDialog.menuTooltip")); //$NON-NLS-1$ 933 viewMenuButton.addSelectionListener(widgetSelectedAdapter(e -> showDialogMenu())); 934 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=177183 935 toolBar.addMouseListener(new MouseAdapter() { 936 @Override 937 public void mouseDown(MouseEvent e) { 938 showDialogMenu(); 939 } 940 }); 941 } 942 943 /** 944 * Fill the dialog's menu. Subclasses may extend or override. 945 * 946 * @param dialogMenu 947 * The dialog's menu. 948 */ fillDialogMenu(IMenuManager dialogMenu)949 protected void fillDialogMenu(IMenuManager dialogMenu) { 950 dialogMenu.add(new GroupMarker("SystemMenuStart")); //$NON-NLS-1$ 951 dialogMenu.add(new MoveAction()); 952 dialogMenu.add(new ResizeAction()); 953 if (showPersistActions) { 954 if (isUsing34API) { 955 dialogMenu.add(new PersistLocationAction()); 956 dialogMenu.add(new PersistSizeAction()); 957 } else { 958 dialogMenu.add(new PersistBoundsAction()); 959 } 960 } 961 dialogMenu.add(new Separator("SystemMenuEnd")); //$NON-NLS-1$ 962 } 963 964 /** 965 * Perform the requested tracker action (resize or move). 966 * 967 * @param style 968 * The track style (resize or move). 969 */ performTrackerAction(int style)970 private void performTrackerAction(int style) { 971 Shell shell = getShell(); 972 if (shell == null || shell.isDisposed()) { 973 return; 974 } 975 976 Tracker tracker = new Tracker(shell.getDisplay(), style); 977 tracker.setStippled(true); 978 Rectangle[] r = new Rectangle[] { shell.getBounds() }; 979 tracker.setRectangles(r); 980 981 // Ignore any deactivate events caused by opening the tracker. 982 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=120656 983 boolean oldListenToDeactivate = listenToDeactivate; 984 listenToDeactivate = false; 985 if (tracker.open()) { 986 if (!shell.isDisposed()) { 987 shell.setBounds(tracker.getRectangles()[0]); 988 } 989 } 990 tracker.dispose(); 991 listenToDeactivate = oldListenToDeactivate; 992 } 993 994 /** 995 * Show the dialog's menu. This message has no effect if the receiver was 996 * not configured to show a menu. Clients may call this method in order to 997 * trigger the menu via keystrokes or other gestures. Subclasses typically 998 * do not override method. 999 */ showDialogMenu()1000 protected void showDialogMenu() { 1001 if (!showDialogMenu) { 1002 return; 1003 } 1004 1005 if (menuManager == null) { 1006 menuManager = new MenuManager(); 1007 fillDialogMenu(menuManager); 1008 } 1009 // Setting this flag works around a problem that remains on X only, 1010 // whereby activating the menu deactivates our shell. 1011 listenToDeactivate = !Util.isGtk(); 1012 1013 Menu menu = menuManager.createContextMenu(getShell()); 1014 Rectangle bounds = toolBar.getBounds(); 1015 Point topLeft = new Point(bounds.x, bounds.y + bounds.height); 1016 topLeft = getShell().toDisplay(topLeft); 1017 menu.setLocation(topLeft.x, topLeft.y); 1018 menu.setVisible(true); 1019 } 1020 1021 /** 1022 * Set the text to be shown in the popup's info area. This message has no 1023 * effect if there was no info text supplied when the dialog first opened. 1024 * Subclasses may override this method. 1025 * 1026 * @param text 1027 * the text to be shown when the info area is displayed. 1028 * 1029 */ setInfoText(String text)1030 protected void setInfoText(String text) { 1031 infoText = text; 1032 if (infoLabel != null) { 1033 infoLabel.setText(text); 1034 } 1035 } 1036 1037 /** 1038 * Set the text to be shown in the popup's title area. This message has no 1039 * effect if there was no title label specified when the dialog was 1040 * originally opened. Subclasses may override this method. 1041 * 1042 * @param text 1043 * the text to be shown when the title area is displayed. 1044 * 1045 */ setTitleText(String text)1046 protected void setTitleText(String text) { 1047 titleText = text; 1048 if (titleLabel != null) { 1049 titleLabel.setText(text); 1050 } 1051 } 1052 1053 /** 1054 * Return a boolean indicating whether this dialog will persist its bounds. This 1055 * value is initially set in the dialog's constructor, but can be modified if 1056 * the persist bounds action is shown on the menu and the user has changed its 1057 * value. Subclasses may override this method. 1058 * 1059 * @return <code>true</code> if the dialog's bounds will be persisted, 1060 * <code>false</code> if it will not. 1061 * 1062 * @deprecated As of 3.4, please use {@link #getPersistLocation()} or 1063 * {@link #getPersistSize()} to determine separately whether size or 1064 * location should be persisted. 1065 * 1066 * @noreference Planned for deletion see 1067 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=531913 1068 */ 1069 @Deprecated getPersistBounds()1070 protected boolean getPersistBounds() { 1071 return persistLocation && persistSize; 1072 } 1073 1074 /** 1075 * Return a boolean indicating whether this dialog will persist its 1076 * location. This value is initially set in the dialog's constructor, but 1077 * can be modified if the persist location action is shown on the menu and 1078 * the user has changed its value. Subclasses may override this method. 1079 * 1080 * @return <code>true</code> if the dialog's location will be persisted, 1081 * <code>false</code> if it will not. 1082 * 1083 * @see #getPersistSize() 1084 * @since 3.4 1085 */ getPersistLocation()1086 protected boolean getPersistLocation() { 1087 return persistLocation; 1088 } 1089 1090 /** 1091 * Return a boolean indicating whether this dialog will persist its size. 1092 * This value is initially set in the dialog's constructor, but can be 1093 * modified if the persist size action is shown on the menu and the user has 1094 * changed its value. Subclasses may override this method. 1095 * 1096 * @return <code>true</code> if the dialog's size will be persisted, 1097 * <code>false</code> if it will not. 1098 * 1099 * @see #getPersistLocation() 1100 * @since 3.4 1101 */ getPersistSize()1102 protected boolean getPersistSize() { 1103 return persistSize; 1104 } 1105 1106 /** 1107 * Opens this window, creating it first if it has not yet been created. 1108 * <p> 1109 * This method is reimplemented for special configuration of PopupDialogs. 1110 * It never blocks on open, immediately returning <code>OK</code> if the 1111 * open is successful, or <code>CANCEL</code> if it is not. It provides 1112 * framework hooks that allow subclasses to set the focus and tab order, and 1113 * avoids the use of <code>shell.open()</code> in cases where the focus 1114 * should not be given to the shell initially. 1115 * 1116 * @return the return code 1117 * 1118 * @see org.eclipse.jface.window.Window#open() 1119 */ 1120 @Override open()1121 public int open() { 1122 1123 Shell shell = getShell(); 1124 if (shell == null || shell.isDisposed()) { 1125 shell = null; 1126 // create the window 1127 create(); 1128 shell = getShell(); 1129 } 1130 1131 // provide a hook for adjusting the bounds. This is only 1132 // necessary when there is content driven sizing that must be 1133 // adjusted each time the dialog is opened. 1134 adjustBounds(); 1135 1136 // limit the shell size to the display size 1137 constrainShellSize(); 1138 1139 // set up the tab order for the dialog 1140 setTabOrder((Composite) getContents()); 1141 1142 // initialize flags for listening to deactivate 1143 listenToDeactivate = false; 1144 listenToParentDeactivate = false; 1145 1146 // open the window 1147 if (takeFocusOnOpen) { 1148 shell.open(); 1149 getFocusControl().setFocus(); 1150 } else { 1151 shell.setVisible(true); 1152 } 1153 1154 return OK; 1155 1156 } 1157 1158 /** 1159 * Closes this window, disposes its shell, and removes this window from its 1160 * window manager (if it has one). 1161 * <p> 1162 * This method is extended to save the dialog bounds and initialize widget 1163 * state so that the widgets can be recreated if the dialog is reopened. 1164 * This method may be extended (<code>super.close</code> must be called). 1165 * </p> 1166 * 1167 * @return <code>true</code> if the window is (or was already) closed, and 1168 * <code>false</code> if it is still open 1169 */ 1170 @Override close()1171 public boolean close() { 1172 // If already closed, there is nothing to do. 1173 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=127505 1174 if (getShell() == null || getShell().isDisposed()) { 1175 return true; 1176 } 1177 1178 saveDialogBounds(getShell()); 1179 // Widgets are about to be disposed, so null out any state 1180 // related to them that was not handled in dispose listeners. 1181 // We do this before disposal so that any received activate or 1182 // deactivate events are duly ignored. 1183 initializeWidgetState(); 1184 1185 if (parentDeactivateListener != null) { 1186 getShell().getParent().removeListener(SWT.Deactivate, 1187 parentDeactivateListener); 1188 parentDeactivateListener = null; 1189 } 1190 1191 return super.close(); 1192 } 1193 1194 /** 1195 * Gets the dialog settings that should be used for remembering the bounds 1196 * of the dialog. Subclasses should override this method when they wish to 1197 * persist the bounds of the dialog. 1198 * 1199 * @return settings the dialog settings used to store the dialog's location 1200 * and/or size, or <code>null</code> if the dialog's bounds should 1201 * never be stored. 1202 */ getDialogSettings()1203 protected IDialogSettings getDialogSettings() { 1204 return null; 1205 } 1206 1207 /** 1208 * Saves the bounds of the shell in the appropriate dialog settings. The 1209 * bounds are recorded relative to the parent shell, if there is one, or 1210 * display coordinates if there is no parent shell. Subclasses typically 1211 * need not override this method, but may extend it (calling 1212 * <code>super.saveDialogBounds</code> if additional bounds information 1213 * should be stored. Clients may also call this method to persist the bounds 1214 * at times other than closing the dialog. 1215 * 1216 * @param shell 1217 * The shell whose bounds are to be stored 1218 */ saveDialogBounds(Shell shell)1219 protected void saveDialogBounds(Shell shell) { 1220 IDialogSettings settings = getDialogSettings(); 1221 if (settings != null) { 1222 Point shellLocation = shell.getLocation(); 1223 Point shellSize = shell.getSize(); 1224 Shell parent = getParentShell(); 1225 if (parent != null) { 1226 Point parentLocation = parent.getLocation(); 1227 shellLocation.x -= parentLocation.x; 1228 shellLocation.y -= parentLocation.y; 1229 } 1230 String prefix = getClass().getName(); 1231 if (persistSize) { 1232 settings.put(prefix + DIALOG_WIDTH, shellSize.x); 1233 settings.put(prefix + DIALOG_HEIGHT, shellSize.y); 1234 } 1235 if (persistLocation) { 1236 settings.put(prefix + DIALOG_ORIGIN_X, shellLocation.x); 1237 settings.put(prefix + DIALOG_ORIGIN_Y, shellLocation.y); 1238 } 1239 if (showPersistActions && showDialogMenu) { 1240 settings.put(getClass().getName() + DIALOG_USE_PERSISTED_SIZE, 1241 persistSize); 1242 settings.put(getClass().getName() 1243 + DIALOG_USE_PERSISTED_LOCATION, persistLocation); 1244 1245 } 1246 } 1247 } 1248 1249 @Override getInitialSize()1250 protected Point getInitialSize() { 1251 Point result = getDefaultSize(); 1252 if (persistSize) { 1253 IDialogSettings settings = getDialogSettings(); 1254 if (settings != null) { 1255 try { 1256 int width = settings.getInt(getClass().getName() 1257 + DIALOG_WIDTH); 1258 int height = settings.getInt(getClass().getName() 1259 + DIALOG_HEIGHT); 1260 result = new Point(width, height); 1261 1262 } catch (NumberFormatException e) { 1263 } 1264 } 1265 } 1266 // No attempt is made to constrain the bounds. The default 1267 // constraining behavior in Window will be used. 1268 return result; 1269 } 1270 1271 /** 1272 * Return the default size to use for the shell. This default size is used 1273 * if the dialog does not have any persisted size to restore. The default 1274 * implementation returns the preferred size of the shell. Subclasses should 1275 * override this method when an alternate default size is desired, rather 1276 * than overriding {@link #getInitialSize()}. 1277 * 1278 * @return the initial size of the shell 1279 * 1280 * @see #getPersistSize() 1281 * @since 3.4 1282 */ getDefaultSize()1283 protected Point getDefaultSize() { 1284 return super.getInitialSize(); 1285 } 1286 1287 /** 1288 * Returns the default location to use for the shell. This default location 1289 * is used if the dialog does not have any persisted location to restore. 1290 * The default implementation uses the location computed by 1291 * {@link org.eclipse.jface.window.Window#getInitialLocation(Point)}. 1292 * Subclasses should override this method when an alternate default location 1293 * is desired, rather than overriding {@link #getInitialLocation(Point)}. 1294 * 1295 * @param initialSize 1296 * the initial size of the shell, as returned by 1297 * <code>getInitialSize</code>. 1298 * @return the initial location of the shell 1299 * 1300 * @see #getPersistLocation() 1301 * @since 3.4 1302 */ getDefaultLocation(Point initialSize)1303 protected Point getDefaultLocation(Point initialSize) { 1304 return super.getInitialLocation(initialSize); 1305 } 1306 1307 /** 1308 * Adjust the bounds of the popup as necessary prior to opening the dialog. 1309 * Default is to do nothing, which honors any bounds set directly by clients 1310 * or those that have been saved in the dialog settings. Subclasses should 1311 * override this method when there are bounds computations that must be 1312 * checked each time the dialog is opened. 1313 */ adjustBounds()1314 protected void adjustBounds() { 1315 } 1316 1317 @Override getInitialLocation(Point initialSize)1318 protected Point getInitialLocation(Point initialSize) { 1319 Point result = getDefaultLocation(initialSize); 1320 if (persistLocation) { 1321 IDialogSettings settings = getDialogSettings(); 1322 if (settings != null) { 1323 try { 1324 int x = settings.getInt(getClass().getName() 1325 + DIALOG_ORIGIN_X); 1326 int y = settings.getInt(getClass().getName() 1327 + DIALOG_ORIGIN_Y); 1328 result = new Point(x, y); 1329 // The coordinates were stored relative to the parent shell. 1330 // Convert to display coordinates. 1331 Shell parent = getParentShell(); 1332 if (parent != null) { 1333 Point parentLocation = parent.getLocation(); 1334 result.x += parentLocation.x; 1335 result.y += parentLocation.y; 1336 } 1337 } catch (NumberFormatException e) { 1338 } 1339 } 1340 } 1341 // No attempt is made to constrain the bounds. The default 1342 // constraining behavior in Window will be used. 1343 return result; 1344 } 1345 1346 /** 1347 * Apply any desired color to the specified composite and its children. 1348 * 1349 * @param composite 1350 * the contents composite 1351 */ applyColors(Composite composite)1352 private void applyColors(Composite composite) { 1353 // The getForeground() and getBackground() methods 1354 // should not answer null, but IColorProvider clients 1355 // are accustomed to null meaning use the default, so we guard 1356 // against this assumption. 1357 Color color = getForeground(); 1358 if (color == null) 1359 color = getDefaultForeground(); 1360 applyForegroundColor(color, composite, getForegroundColorExclusions()); 1361 color = getBackground(); 1362 if (color == null) 1363 color = getDefaultBackground(); 1364 applyBackgroundColor(color, composite, getBackgroundColorExclusions()); 1365 } 1366 1367 /** 1368 * Get the foreground color that should be used for this popup. Subclasses 1369 * may override. 1370 * 1371 * @return the foreground color to be used. Should not be <code>null</code>. 1372 * 1373 * @since 3.4 1374 * 1375 * @see #getForegroundColorExclusions() 1376 */ getForeground()1377 protected Color getForeground() { 1378 return getDefaultForeground(); 1379 } 1380 1381 /** 1382 * Get the background color that should be used for this popup. Subclasses 1383 * may override. 1384 * 1385 * @return the background color to be used. Should not be <code>null</code>. 1386 * 1387 * @since 3.4 1388 * 1389 * @see #getBackgroundColorExclusions() 1390 */ getBackground()1391 protected Color getBackground() { 1392 return getDefaultBackground(); 1393 } 1394 1395 /** 1396 * Return the default foreground color used for popup dialogs. 1397 * 1398 * @return the default foreground color. 1399 */ getDefaultForeground()1400 private Color getDefaultForeground() { 1401 if ((getShellStyle() & SWT.NO_FOCUS) != 0) { 1402 return JFaceResources.getColorRegistry().get(JFacePreferences.INFORMATION_FOREGROUND_COLOR); 1403 } 1404 return JFaceResources.getColorRegistry().get(JFacePreferences.CONTENT_ASSIST_FOREGROUND_COLOR); 1405 } 1406 1407 /** 1408 * Return the default background color used for popup dialogs. 1409 * 1410 * @return the default background color 1411 */ getDefaultBackground()1412 private Color getDefaultBackground() { 1413 if ((getShellStyle() & SWT.NO_FOCUS) != 0) { 1414 return JFaceResources.getColorRegistry().get(JFacePreferences.INFORMATION_BACKGROUND_COLOR); 1415 } 1416 return JFaceResources.getColorRegistry().get(JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR); 1417 } 1418 1419 /** 1420 * Apply any desired fonts to the specified composite and its children. 1421 * 1422 * @param composite 1423 * the contents composite 1424 */ applyFonts(Composite composite)1425 private void applyFonts(Composite composite) { 1426 Dialog.applyDialogFont(composite); 1427 1428 if (titleLabel != null) { 1429 Font font = titleLabel.getFont(); 1430 FontData[] fontDatas = font.getFontData(); 1431 for (FontData fontData : fontDatas) { 1432 fontData.setStyle(SWT.BOLD); 1433 } 1434 titleFont = new Font(titleLabel.getDisplay(), fontDatas); 1435 titleLabel.setFont(titleFont); 1436 } 1437 1438 if (infoLabel != null) { 1439 Font font = infoLabel.getFont(); 1440 FontData[] fontDatas = font.getFontData(); 1441 for (FontData fontData : fontDatas) { 1442 fontData.setHeight(fontData.getHeight() * 9 / 10); 1443 } 1444 infoFont = new Font(infoLabel.getDisplay(), fontDatas); 1445 infoLabel.setFont(infoFont); 1446 } 1447 } 1448 1449 /** 1450 * Set the specified foreground color for the specified control and all of 1451 * its children, except for those specified in the list of exclusions. 1452 * 1453 * @param color 1454 * the color to use as the foreground color 1455 * @param control 1456 * the control whose color is to be changed 1457 * @param exclusions 1458 * a list of controls who are to be excluded from getting their 1459 * color assigned 1460 */ applyForegroundColor(Color color, Control control, List<Control> exclusions)1461 private void applyForegroundColor(Color color, Control control, 1462 List<Control> exclusions) { 1463 if (!exclusions.contains(control)) { 1464 control.setForeground(color); 1465 } 1466 if (control instanceof Composite) { 1467 Control[] children = ((Composite) control).getChildren(); 1468 for (Control element : children) { 1469 applyForegroundColor(color, element, exclusions); 1470 } 1471 } 1472 } 1473 1474 /** 1475 * Set the specified background color for the specified control and all of 1476 * its children, except for those specified in the list of exclusions. 1477 * 1478 * @param color 1479 * the color to use as the background color 1480 * @param control 1481 * the control whose color is to be changed 1482 * @param exclusions 1483 * a list of controls who are to be excluded from getting their 1484 * color assigned 1485 */ applyBackgroundColor(Color color, Control control, List<Control> exclusions)1486 private void applyBackgroundColor(Color color, Control control, 1487 List<Control> exclusions) { 1488 if (!exclusions.contains(control)) { 1489 control.setBackground(color); 1490 } 1491 if (control instanceof Composite) { 1492 Control[] children = ((Composite) control).getChildren(); 1493 for (Control element : children) { 1494 applyBackgroundColor(color, element, exclusions); 1495 } 1496 } 1497 } 1498 1499 /** 1500 * Set the specified foreground color for the specified control and all of 1501 * its children. Subclasses may override this method, but typically do not. 1502 * If a subclass wishes to exclude a particular control in its contents from 1503 * getting the specified foreground color, it may instead override 1504 * {@link #getForegroundColorExclusions()}. 1505 * 1506 * @param color 1507 * the color to use as the foreground color 1508 * @param control 1509 * the control whose color is to be changed 1510 * @see PopupDialog#getForegroundColorExclusions() 1511 */ applyForegroundColor(Color color, Control control)1512 protected void applyForegroundColor(Color color, Control control) { 1513 applyForegroundColor(color, control, getForegroundColorExclusions()); 1514 } 1515 1516 /** 1517 * Set the specified background color for the specified control and all of 1518 * its children. Subclasses may override this method, but typically do not. 1519 * If a subclass wishes to exclude a particular control in its contents from 1520 * getting the specified background color, it may instead override 1521 * {@link #getBackgroundColorExclusions()} 1522 * 1523 * @param color 1524 * the color to use as the background color 1525 * @param control 1526 * the control whose color is to be changed 1527 * @see PopupDialog#getBackgroundColorExclusions() 1528 */ applyBackgroundColor(Color color, Control control)1529 protected void applyBackgroundColor(Color color, Control control) { 1530 applyBackgroundColor(color, control, getBackgroundColorExclusions()); 1531 } 1532 1533 /** 1534 * Return a list of controls which should never have their foreground color 1535 * reset. Subclasses may extend this method, but should always call 1536 * <code>super.getForegroundColorExclusions</code> to aggregate the list. 1537 * 1538 * 1539 * @return the List of controls 1540 */ getForegroundColorExclusions()1541 protected List<Control> getForegroundColorExclusions() { 1542 List<Control> list = new ArrayList<>(3); 1543 if (infoLabel != null) { 1544 list.add(infoLabel); 1545 } 1546 if (titleSeparator != null) { 1547 list.add(titleSeparator); 1548 } 1549 if (infoSeparator != null) { 1550 list.add(infoSeparator); 1551 } 1552 return list; 1553 } 1554 1555 /** 1556 * Return a list of controls which should never have their background color 1557 * reset. Subclasses may extend this method, but should always call 1558 * <code>super.getBackgroundColorExclusions</code> to aggregate the list. 1559 * 1560 * @return the List of controls 1561 */ getBackgroundColorExclusions()1562 protected List<Control> getBackgroundColorExclusions() { 1563 List<Control> list = new ArrayList<>(2); 1564 if (titleSeparator != null) { 1565 list.add(titleSeparator); 1566 } 1567 if (infoSeparator != null) { 1568 list.add(infoSeparator); 1569 } 1570 return list; 1571 } 1572 1573 /** 1574 * Initialize any state related to the widgetry that should be set up each 1575 * time widgets are created. 1576 */ initializeWidgetState()1577 private void initializeWidgetState() { 1578 menuManager = null; 1579 dialogArea = null; 1580 titleLabel = null; 1581 titleSeparator = null; 1582 infoSeparator = null; 1583 infoLabel = null; 1584 toolBar = null; 1585 1586 // If the menu item for persisting bounds is displayed, use the stored 1587 // value to determine whether any persisted bounds should be honored at 1588 // all. 1589 if (showDialogMenu && showPersistActions) { 1590 IDialogSettings settings = getDialogSettings(); 1591 if (settings != null) { 1592 String key = getClass().getName() + DIALOG_USE_PERSISTED_SIZE; 1593 if (settings.get(key) != null || !isUsing34API) 1594 persistSize = settings.getBoolean(key); 1595 key = getClass().getName() + DIALOG_USE_PERSISTED_LOCATION; 1596 if (settings.get(key) != null || !isUsing34API) 1597 persistLocation = settings.getBoolean(key); 1598 } 1599 } 1600 } 1601 1602 /** 1603 * The dialog is being disposed. Dispose of any resources allocated. 1604 * 1605 */ handleDispose()1606 private void handleDispose() { 1607 if (infoColor != null && !infoColor.isDisposed()) { 1608 infoColor.dispose(); 1609 } 1610 infoColor = null; 1611 if (infoFont != null && !infoFont.isDisposed()) { 1612 infoFont.dispose(); 1613 } 1614 infoFont = null; 1615 if (titleFont != null && !titleFont.isDisposed()) { 1616 titleFont.dispose(); 1617 } 1618 titleFont = null; 1619 } 1620 } 1621