comparison dwtx/jface/dialogs/Dialog.d @ 8:a3ff22a98bef

Dialog
author Frank Benoit <benoit@tionex.de>
date Sat, 29 Mar 2008 01:25:27 +0100
parents
children ea8ff534f622
comparison
equal deleted inserted replaced
7:8a302fdb4140 8:a3ff22a98bef
1 /*******************************************************************************
2 * Copyright (c) 2000, 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.jface.dialogs.Dialog;
14
15 import dwtx.jface.dialogs.IDialogBlockedHandler;
16 import dwtx.jface.dialogs.IDialogSettings;
17 import dwtx.jface.dialogs.IDialogConstants;
18 // import tango.util.Arrays;
19
20 import dwt.DWT;
21 import dwt.events.SelectionAdapter;
22 import dwt.events.SelectionEvent;
23 import dwt.graphics.Font;
24 import dwt.graphics.FontData;
25 import dwt.graphics.FontMetrics;
26 import dwt.graphics.GC;
27 import dwt.graphics.Image;
28 import dwt.graphics.Point;
29 import dwt.layout.FormData;
30 import dwt.layout.GridData;
31 import dwt.layout.GridLayout;
32 import dwt.widgets.Button;
33 import dwt.widgets.Composite;
34 import dwt.widgets.Control;
35 import dwt.widgets.Display;
36 import dwt.widgets.Shell;
37 import dwtx.core.runtime.IProgressMonitor;
38 import dwtx.core.runtime.IStatus;
39 import dwtx.core.runtime.Status;
40 import dwtx.jface.resource.JFaceResources;
41 import dwtx.jface.util.Policy;
42 import dwtx.jface.window.IShellProvider;
43 import dwtx.jface.window.SameShellProvider;
44 import dwtx.jface.window.Window;
45
46 import dwt.dwthelper.utils;
47
48 /**
49 * A dialog is a specialized window used for narrow-focused communication with
50 * the user.
51 * <p>
52 * Dialogs are usually modal. Consequently, it is generally bad practice to open
53 * a dialog without a parent. A modal dialog without a parent is not prevented
54 * from disappearing behind the application's other windows, making it very
55 * confusing for the user.
56 * </p>
57 * <p>
58 * If there is more than one modal dialog is open the second one should be
59 * parented off of the shell of the first one otherwise it is possible that the
60 * OS will give focus to the first dialog potentially blocking the UI.
61 * </p>
62 */
63 public abstract class Dialog : Window {
64 /**
65 * Image registry key for error image (value
66 * <code>"dialog_error_image"</code>).
67 *
68 * @deprecated use
69 * dwt.widgets.Display.getSystemImage(DWT.ICON_ERROR)
70 */
71 public static const String DLG_IMG_ERROR = "dialog_error_image"; //$NON-NLS-1$
72
73 /**
74 * Image registry key for info image (value <code>"dialog_info_image"</code>).
75 *
76 * @deprecated use
77 * dwt.widgets.Display.getSystemImage(DWT.ICON_INFORMATION)
78 */
79 public static const String DLG_IMG_INFO = "dialog_info_imageg"; //$NON-NLS-1$
80
81 /**
82 * Image registry key for question image (value
83 * <code>"dialog_question_image"</code>).
84 *
85 * @deprecated dwt.widgets.Display.getSystemImage(DWT.ICON_QUESTION)
86 */
87 public static const String DLG_IMG_QUESTION = "dialog_question_image"; //$NON-NLS-1$
88
89 /**
90 * Image registry key for warning image (value
91 * <code>"dialog_warning_image"</code>).
92 *
93 * @deprecated use
94 * dwt.widgets.Display.getSystemImage(DWT.ICON_WARNING)
95 */
96 public static const String DLG_IMG_WARNING = "dialog_warning_image"; //$NON-NLS-1$
97
98 /**
99 * Image registry key for info message image (value
100 * <code>"dialog_messasge_info_image"</code>).
101 *
102 * @since 2.0
103 */
104 public static const String DLG_IMG_MESSAGE_INFO = "dialog_messasge_info_image"; //$NON-NLS-1$
105
106 /**
107 * Image registry key for info message image (value
108 * <code>"dialog_messasge_warning_image"</code>).
109 *
110 * @since 2.0
111 */
112 public static const String DLG_IMG_MESSAGE_WARNING = "dialog_messasge_warning_image"; //$NON-NLS-1$
113
114 /**
115 * Image registry key for info message image (value
116 * <code>"dialog_message_error_image"</code>).
117 *
118 * @since 2.0
119 */
120 public static const String DLG_IMG_MESSAGE_ERROR = "dialog_message_error_image"; //$NON-NLS-1$
121
122 /**
123 * Image registry key for help image (value
124 * <code>"dialog_help_image"</code>).
125 *
126 * @since 3.2
127 */
128 public static const String DLG_IMG_HELP = "dialog_help_image"; //$NON-NLS-1$
129
130 /**
131 * The ellipsis is the string that is used to represent shortened text.
132 *
133 * @since 3.0
134 */
135 public static const String ELLIPSIS = "..."; //$NON-NLS-1$
136
137 /**
138 * The dialog settings key name for stored dialog x location.
139 *
140 * @since 3.2
141 */
142 private static const String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$
143
144 /**
145 * The dialog settings key name for stored dialog y location.
146 *
147 * @since 3.2
148 */
149 private static const String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$
150
151 /**
152 * The dialog settings key name for stored dialog width.
153 *
154 * @since 3.2
155 */
156 private static const String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
157
158 /**
159 * The dialog settings key name for stored dialog height.
160 *
161 * @since 3.2
162 */
163 private static const String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
164
165 /**
166 * The dialog settings key name for the font used when the dialog
167 * height and width was stored.
168 *
169 *@since 3.2
170 */
171 private static const String DIALOG_FONT_DATA = "DIALOG_FONT_NAME"; //$NON-NLS-1$
172
173 /**
174 * A value that can be used for stored dialog width or height that
175 * indicates that the default bounds should be used.
176 *
177 * @since 3.2
178 */
179 public static const int DIALOG_DEFAULT_BOUNDS = -1;
180
181 /**
182 * Constants that can be used for specifying the strategy for persisting
183 * dialog bounds. These constants represent bit masks that can be used
184 * together.
185 *
186 *@since 3.2
187 */
188
189 /**
190 * Persist the last location of the dialog.
191 * @since 3.2
192 */
193 public static const int DIALOG_PERSISTLOCATION = 0x0001;
194 /**
195 * Persist the last known size of the dialog.
196 * @since 3.2
197 */
198 public static const int DIALOG_PERSISTSIZE = 0x0002;
199
200 /**
201 * The dialog area; <code>null</code> until dialog is layed out.
202 */
203 protected Control dialogArea;
204
205 /**
206 * The button bar; <code>null</code> until dialog is layed out.
207 */
208 public Control buttonBar;
209
210 /**
211 * Collection of buttons created by the <code>createButton</code> method.
212 */
213 private Button[int] buttons;
214
215 /**
216 * Font metrics to use for determining pixel sizes.
217 */
218 private FontMetrics fontMetrics;
219
220 /**
221 * Number of horizontal dialog units per character, value <code>4</code>.
222 */
223 private static const int HORIZONTAL_DIALOG_UNIT_PER_CHAR = 4;
224
225 /**
226 * Number of vertical dialog units per character, value <code>8</code>.
227 */
228 private static const int VERTICAL_DIALOG_UNITS_PER_CHAR = 8;
229
230 /**
231 * Returns the number of pixels corresponding to the height of the given
232 * number of characters.
233 * <p>
234 * The required <code>FontMetrics</code> parameter may be created in the
235 * following way: <code>
236 * GC gc = new GC(control);
237 * gc.setFont(control.getFont());
238 * fontMetrics = gc.getFontMetrics();
239 * gc.dispose();
240 * </code>
241 * </p>
242 *
243 * @param fontMetrics
244 * used in performing the conversion
245 * @param chars
246 * the number of characters
247 * @return the number of pixels
248 * @since 2.0
249 */
250 public static int convertHeightInCharsToPixels(FontMetrics fontMetrics,
251 int chars) {
252 return fontMetrics.getHeight() * chars;
253 }
254
255 /**
256 * Returns the number of pixels corresponding to the given number of
257 * horizontal dialog units.
258 * <p>
259 * The required <code>FontMetrics</code> parameter may be created in the
260 * following way: <code>
261 * GC gc = new GC(control);
262 * gc.setFont(control.getFont());
263 * fontMetrics = gc.getFontMetrics();
264 * gc.dispose();
265 * </code>
266 * </p>
267 *
268 * @param fontMetrics
269 * used in performing the conversion
270 * @param dlus
271 * the number of horizontal dialog units
272 * @return the number of pixels
273 * @since 2.0
274 */
275 public static int convertHorizontalDLUsToPixels(FontMetrics fontMetrics,
276 int dlus) {
277 // round to the nearest pixel
278 return (fontMetrics.getAverageCharWidth() * dlus + HORIZONTAL_DIALOG_UNIT_PER_CHAR / 2)
279 / HORIZONTAL_DIALOG_UNIT_PER_CHAR;
280 }
281
282 /**
283 * Returns the number of pixels corresponding to the given number of
284 * vertical dialog units.
285 * <p>
286 * The required <code>FontMetrics</code> parameter may be created in the
287 * following way: <code>
288 * GC gc = new GC(control);
289 * gc.setFont(control.getFont());
290 * fontMetrics = gc.getFontMetrics();
291 * gc.dispose();
292 * </code>
293 * </p>
294 *
295 * @param fontMetrics
296 * used in performing the conversion
297 * @param dlus
298 * the number of vertical dialog units
299 * @return the number of pixels
300 * @since 2.0
301 */
302 public static int convertVerticalDLUsToPixels(FontMetrics fontMetrics,
303 int dlus) {
304 // round to the nearest pixel
305 return (fontMetrics.getHeight() * dlus + VERTICAL_DIALOG_UNITS_PER_CHAR / 2)
306 / VERTICAL_DIALOG_UNITS_PER_CHAR;
307 }
308
309 /**
310 * Returns the number of pixels corresponding to the width of the given
311 * number of characters.
312 * <p>
313 * The required <code>FontMetrics</code> parameter may be created in the
314 * following way: <code>
315 * GC gc = new GC(control);
316 * gc.setFont(control.getFont());
317 * fontMetrics = gc.getFontMetrics();
318 * gc.dispose();
319 * </code>
320 * </p>
321 *
322 * @param fontMetrics
323 * used in performing the conversion
324 * @param chars
325 * the number of characters
326 * @return the number of pixels
327 * @since 2.0
328 */
329 public static int convertWidthInCharsToPixels(FontMetrics fontMetrics,
330 int chars) {
331 return fontMetrics.getAverageCharWidth() * chars;
332 }
333
334 /**
335 * Shortens the given text <code>textValue</code> so that its width in
336 * pixels does not exceed the width of the given control. Overrides
337 * characters in the center of the original string with an ellipsis ("...")
338 * if necessary. If a <code>null</code> value is given, <code>null</code>
339 * is returned.
340 *
341 * @param textValue
342 * the original string or <code>null</code>
343 * @param control
344 * the control the string will be displayed on
345 * @return the string to display, or <code>null</code> if null was passed
346 * in
347 *
348 * @since 3.0
349 */
350 public static String shortenText(String textValue, Control control) {
351 if (textValue is null) {
352 return null;
353 }
354 GC gc = new GC(control);
355 int maxWidth = control.getBounds().width - 5;
356 int maxExtent = gc.textExtent(textValue).x;
357 if (maxExtent < maxWidth) {
358 gc.dispose();
359 return textValue;
360 }
361 int length = textValue.length;
362 int charsToClip = cast(int) Math.round(0.95f*length * (1 - (cast(float)maxWidth/maxExtent)));
363 int pivot = length / 2;
364 int start = pivot - (charsToClip/2);
365 int end = pivot + (charsToClip/2) + 1;
366 while (start >= 0 && end < length) {
367 String s1 = textValue.substring(0, start);
368 String s2 = textValue.substring(end, length);
369 String s = s1 ~ ELLIPSIS ~ s2;
370 int l = gc.textExtent(s).x;
371 if (l < maxWidth) {
372 gc.dispose();
373 return s;
374 }
375 start--;
376 end++;
377 }
378 gc.dispose();
379 return textValue;
380 }
381
382 /**
383 * Create a default instance of the blocked handler which does not do
384 * anything.
385 */
386 public static IDialogBlockedHandler blockedHandler;
387 static this(){
388 blockedHandler = new class IDialogBlockedHandler {
389 /*
390 * (non-Javadoc)
391 *
392 * @see dwtx.jface.dialogs.IDialogBlockedHandler#clearBlocked()
393 */
394 public void clearBlocked() {
395 // No default behaviour
396 }
397
398 /*
399 * (non-Javadoc)
400 *
401 * @see dwtx.jface.dialogs.IDialogBlockedHandler#showBlocked(dwtx.core.runtime.IProgressMonitor,
402 * dwtx.core.runtime.IStatus, java.lang.String)
403 */
404 public void showBlocked(IProgressMonitor blocking,
405 IStatus blockingStatus, String blockedName) {
406 // No default behaviour
407 }
408
409 /*
410 * (non-Javadoc)
411 *
412 * @see dwtx.jface.dialogs.IDialogBlockedHandler#showBlocked(dwt.widgets.Shell,
413 * dwtx.core.runtime.IProgressMonitor,
414 * dwtx.core.runtime.IStatus, java.lang.String)
415 */
416 public void showBlocked(Shell parentShell, IProgressMonitor blocking,
417 IStatus blockingStatus, String blockedName) {
418 // No default behaviour
419 }
420 };
421 }
422
423 /**
424 * Creates a dialog instance. Note that the window will have no visual
425 * representation (no widgets) until it is told to open. By default,
426 * <code>open</code> blocks for dialogs.
427 *
428 * @param parentShell
429 * the parent shell, or <code>null</code> to create a top-level
430 * shell
431 */
432 protected this(Shell parentShell) {
433 this(new SameShellProvider(parentShell));
434 if (parentShell is null && Policy.DEBUG_DIALOG_NO_PARENT) {
435 Policy.getLog().log(
436 new Status(IStatus.INFO, Policy.JFACE, IStatus.INFO, this
437 .classinfo.name
438 ~ " created with no shell",//$NON-NLS-1$
439 new Exception( null, null )));
440 }
441 }
442
443 /**
444 * Creates a dialog with the given parent.
445 *
446 * @param parentShell
447 * object that returns the current parent shell
448 *
449 * @since 3.1
450 */
451 protected this(IShellProvider parentShell) {
452 super(parentShell);
453 setShellStyle(DWT.DIALOG_TRIM | DWT.APPLICATION_MODAL
454 | getDefaultOrientation());
455 setBlockOnOpen(true);
456 }
457
458 /**
459 * Notifies that this dialog's button with the given id has been pressed.
460 * <p>
461 * The <code>Dialog</code> implementation of this framework method calls
462 * <code>okPressed</code> if the ok button is the pressed, and
463 * <code>cancelPressed</code> if the cancel button is the pressed. All
464 * other button presses are ignored. Subclasses may override to handle other
465 * buttons, but should call <code>super.buttonPressed</code> if the
466 * default handling of the ok and cancel buttons is desired.
467 * </p>
468 *
469 * @param buttonId
470 * the id of the button that was pressed (see
471 * <code>IDialogConstants.*_ID</code> constants)
472 */
473 protected void buttonPressed(int buttonId) {
474 if (IDialogConstants.OK_ID is buttonId) {
475 okPressed();
476 } else if (IDialogConstants.CANCEL_ID is buttonId) {
477 cancelPressed();
478 }
479 }
480
481 /**
482 * Notifies that the cancel button of this dialog has been pressed.
483 * <p>
484 * The <code>Dialog</code> implementation of this framework method sets
485 * this dialog's return code to <code>Window.CANCEL</code> and closes the
486 * dialog. Subclasses may override if desired.
487 * </p>
488 */
489 protected void cancelPressed() {
490 setReturnCode(CANCEL);
491 close();
492 }
493
494 /**
495 * Returns the number of pixels corresponding to the height of the given
496 * number of characters.
497 * <p>
498 * This method may only be called after <code>initializeDialogUnits</code>
499 * has been called.
500 * </p>
501 * <p>
502 * Clients may call this framework method, but should not override it.
503 * </p>
504 *
505 * @param chars
506 * the number of characters
507 * @return the number of pixels
508 */
509 protected int convertHeightInCharsToPixels(int chars) {
510 // test for failure to initialize for backward compatibility
511 if (fontMetrics is null) {
512 return 0;
513 }
514 return convertHeightInCharsToPixels(fontMetrics, chars);
515 }
516
517 /**
518 * Returns the number of pixels corresponding to the given number of
519 * horizontal dialog units.
520 * <p>
521 * This method may only be called after <code>initializeDialogUnits</code>
522 * has been called.
523 * </p>
524 * <p>
525 * Clients may call this framework method, but should not override it.
526 * </p>
527 *
528 * @param dlus
529 * the number of horizontal dialog units
530 * @return the number of pixels
531 */
532 protected int convertHorizontalDLUsToPixels(int dlus) {
533 // test for failure to initialize for backward compatibility
534 if (fontMetrics is null) {
535 return 0;
536 }
537 return convertHorizontalDLUsToPixels(fontMetrics, dlus);
538 }
539
540 /**
541 * Returns the number of pixels corresponding to the given number of
542 * vertical dialog units.
543 * <p>
544 * This method may only be called after <code>initializeDialogUnits</code>
545 * has been called.
546 * </p>
547 * <p>
548 * Clients may call this framework method, but should not override it.
549 * </p>
550 *
551 * @param dlus
552 * the number of vertical dialog units
553 * @return the number of pixels
554 */
555 protected int convertVerticalDLUsToPixels(int dlus) {
556 // test for failure to initialize for backward compatibility
557 if (fontMetrics is null) {
558 return 0;
559 }
560 return convertVerticalDLUsToPixels(fontMetrics, dlus);
561 }
562
563 /**
564 * Returns the number of pixels corresponding to the width of the given
565 * number of characters.
566 * <p>
567 * This method may only be called after <code>initializeDialogUnits</code>
568 * has been called.
569 * </p>
570 * <p>
571 * Clients may call this framework method, but should not override it.
572 * </p>
573 *
574 * @param chars
575 * the number of characters
576 * @return the number of pixels
577 */
578 protected int convertWidthInCharsToPixels(int chars) {
579 // test for failure to initialize for backward compatibility
580 if (fontMetrics is null) {
581 return 0;
582 }
583 return convertWidthInCharsToPixels(fontMetrics, chars);
584 }
585
586 /**
587 * Creates a new button with the given id.
588 * <p>
589 * The <code>Dialog</code> implementation of this framework method creates
590 * a standard push button, registers it for selection events including
591 * button presses, and registers default buttons with its shell. The button
592 * id is stored as the button's client data. If the button id is
593 * <code>IDialogConstants.CANCEL_ID</code>, the new button will be
594 * accessible from <code>getCancelButton()</code>. If the button id is
595 * <code>IDialogConstants.OK_ID</code>, the new button will be accesible
596 * from <code>getOKButton()</code>. Note that the parent's layout is
597 * assumed to be a <code>GridLayout</code> and the number of columns in
598 * this layout is incremented. Subclasses may override.
599 * </p>
600 *
601 * @param parent
602 * the parent composite
603 * @param id
604 * the id of the button (see <code>IDialogConstants.*_ID</code>
605 * constants for standard dialog button ids)
606 * @param label
607 * the label from the button
608 * @param defaultButton
609 * <code>true</code> if the button is to be the default button,
610 * and <code>false</code> otherwise
611 *
612 * @return the new button
613 *
614 * @see #getCancelButton
615 * @see #getOKButton()
616 */
617 protected Button createButton(Composite parent, int id, String label,
618 bool defaultButton) {
619 // increment the number of columns in the button bar
620 (cast(GridLayout) parent.getLayout()).numColumns++;
621 Button button = new Button(parent, DWT.PUSH);
622 button.setText(label);
623 button.setFont(JFaceResources.getDialogFont());
624 button.setData(new ValueWrapperInt(id));
625 button.addSelectionListener(new class SelectionAdapter {
626 public void widgetSelected(SelectionEvent event) {
627 buttonPressed((cast(ValueWrapperInt) event.widget.getData()).value);
628 }
629 });
630 if (defaultButton) {
631 Shell shell = parent.getShell();
632 if (shell !is null) {
633 shell.setDefaultButton(button);
634 }
635 }
636 buttons[id] = button;
637 setButtonLayoutData(button);
638 return button;
639 }
640
641 /**
642 * Creates and returns the contents of this dialog's button bar.
643 * <p>
644 * The <code>Dialog</code> implementation of this framework method lays
645 * out a button bar and calls the <code>createButtonsForButtonBar</code>
646 * framework method to populate it. Subclasses may override.
647 * </p>
648 * <p>
649 * The returned control's layout data must be an instance of
650 * <code>GridData</code>.
651 * </p>
652 *
653 * @param parent
654 * the parent composite to contain the button bar
655 * @return the button bar control
656 */
657 protected Control createButtonBar(Composite parent) {
658 Composite composite = new Composite(parent, DWT.NONE);
659 // create a layout with spacing and margins appropriate for the font
660 // size.
661 GridLayout layout = new GridLayout();
662 layout.numColumns = 0; // this is incremented by createButton
663 layout.makeColumnsEqualWidth = true;
664 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
665 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
666 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
667 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
668 composite.setLayout(layout);
669 GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END
670 | GridData.VERTICAL_ALIGN_CENTER);
671 composite.setLayoutData(data);
672 composite.setFont(parent.getFont());
673
674 // Add the buttons to the button bar.
675 createButtonsForButtonBar(composite);
676 return composite;
677 }
678
679 /**
680 * Adds buttons to this dialog's button bar.
681 * <p>
682 * The <code>Dialog</code> implementation of this framework method adds
683 * standard ok and cancel buttons using the <code>createButton</code>
684 * framework method. These standard buttons will be accessible from
685 * <code>getCancelButton</code>, and <code>getOKButton</code>.
686 * Subclasses may override.
687 * </p>
688 *
689 * @param parent
690 * the button bar composite
691 */
692 protected void createButtonsForButtonBar(Composite parent) {
693 // create OK and Cancel buttons by default
694 createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
695 true);
696 createButton(parent, IDialogConstants.CANCEL_ID,
697 IDialogConstants.CANCEL_LABEL, false);
698 }
699
700 /*
701 * @see Window.initializeBounds()
702 */
703 protected void initializeBounds() {
704 String platform = DWT.getPlatform();
705 if ("carbon".equals(platform)) { //$NON-NLS-1$
706 // On Mac OS X the default button must be the right-most button
707 Shell shell = getShell();
708 if (shell !is null) {
709 Button defaultButton = shell.getDefaultButton();
710 if (defaultButton !is null
711 && isContained(buttonBar, defaultButton)) {
712 defaultButton.moveBelow(null);
713 }
714 }
715 }
716
717 super.initializeBounds();
718 }
719
720 /**
721 * Returns true if the given Control is a direct or indirect child of
722 * container.
723 *
724 * @param container
725 * the potential parent
726 * @param control
727 * @return bool <code>true</code> if control is a child of container
728 */
729 private bool isContained(Control container, Control control) {
730 Composite parent;
731 while ((parent = control.getParent()) !is null) {
732 if (parent is container) {
733 return true;
734 }
735 control = parent;
736 }
737 return false;
738 }
739
740 /**
741 * The <code>Dialog</code> implementation of this <code>Window</code>
742 * method creates and lays out the top level composite for the dialog, and
743 * determines the appropriate horizontal and vertical dialog units based on
744 * the font size. It then calls the <code>createDialogArea</code> and
745 * <code>createButtonBar</code> methods to create the dialog area and
746 * button bar, respectively. Overriding <code>createDialogArea</code> and
747 * <code>createButtonBar</code> are recommended rather than overriding
748 * this method.
749 */
750 protected Control createContents(Composite parent) {
751 // create the top level composite for the dialog
752 Composite composite = new Composite(parent, 0);
753 GridLayout layout = new GridLayout();
754 layout.marginHeight = 0;
755 layout.marginWidth = 0;
756 layout.verticalSpacing = 0;
757 composite.setLayout(layout);
758 composite.setLayoutData(new GridData(GridData.FILL_BOTH));
759 applyDialogFont(composite);
760 // initialize the dialog units
761 initializeDialogUnits(composite);
762 // create the dialog area and button bar
763 dialogArea = createDialogArea(composite);
764 buttonBar = createButtonBar(composite);
765
766 return composite;
767 }
768
769 /**
770 * Creates and returns the contents of the upper part of this dialog (above
771 * the button bar).
772 * <p>
773 * The <code>Dialog</code> implementation of this framework method creates
774 * and returns a new <code>Composite</code> with standard margins and
775 * spacing.
776 * </p>
777 * <p>
778 * The returned control's layout data must be an instance of
779 * <code>GridData</code>. This method must not modify the parent's
780 * layout.
781 * </p>
782 * <p>
783 * Subclasses must override this method but may call <code>super</code> as
784 * in the following example:
785 * </p>
786 *
787 * <pre>
788 * Composite composite = (Composite) super.createDialogArea(parent);
789 * //add controls to composite as necessary
790 * return composite;
791 * </pre>
792 *
793 * @param parent
794 * the parent composite to contain the dialog area
795 * @return the dialog area control
796 */
797 protected Control createDialogArea(Composite parent) {
798 // create a composite with standard margins and spacing
799 Composite composite = new Composite(parent, DWT.NONE);
800 GridLayout layout = new GridLayout();
801 layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
802 layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
803 layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
804 layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
805 composite.setLayout(layout);
806 composite.setLayoutData(new GridData(GridData.FILL_BOTH));
807 applyDialogFont(composite);
808 return composite;
809 }
810
811 /**
812 * Returns the button created by the method <code>createButton</code> for
813 * the specified ID as defined on <code>IDialogConstants</code>. If
814 * <code>createButton</code> was never called with this ID, or if
815 * <code>createButton</code> is overridden, this method will return
816 * <code>null</code>.
817 *
818 * @param id
819 * the id of the button to look for
820 *
821 * @return the button for the ID or <code>null</code>
822 *
823 * @see #createButton(Composite, int, String, bool)
824 * @since 2.0
825 */
826 protected Button getButton(int id) {
827 return buttons[id];
828 }
829
830 /**
831 * Returns the button bar control.
832 * <p>
833 * Clients may call this framework method, but should not override it.
834 * </p>
835 *
836 * @return the button bar, or <code>null</code> if the button bar has not
837 * been created yet
838 */
839 protected Control getButtonBar() {
840 return buttonBar;
841 }
842
843 /**
844 * Returns the button created when <code>createButton</code> is called
845 * with an ID of <code>IDialogConstants.CANCEL_ID</code>. If
846 * <code>createButton</code> was never called with this parameter, or if
847 * <code>createButton</code> is overridden, <code>getCancelButton</code>
848 * will return <code>null</code>.
849 *
850 * @return the cancel button or <code>null</code>
851 *
852 * @see #createButton(Composite, int, String, bool)
853 * @since 2.0
854 * @deprecated Use <code>getButton(IDialogConstants.CANCEL_ID)</code>
855 * instead. This method will be removed soon.
856 */
857 protected Button getCancelButton() {
858 return getButton(IDialogConstants.CANCEL_ID);
859 }
860
861 /**
862 * Returns the dialog area control.
863 * <p>
864 * Clients may call this framework method, but should not override it.
865 * </p>
866 *
867 * @return the dialog area, or <code>null</code> if the dialog area has
868 * not been created yet
869 */
870 protected Control getDialogArea() {
871 return dialogArea;
872 }
873
874 /**
875 * Returns the standard dialog image with the given key. Note that these
876 * images are managed by the dialog framework, and must not be disposed by
877 * another party.
878 *
879 * @param key
880 * one of the <code>Dialog.DLG_IMG_* </code> constants
881 * @return the standard dialog image
882 *
883 * NOTE: Dialog does not use the following images in the registry
884 * DLG_IMG_ERROR DLG_IMG_INFO DLG_IMG_QUESTION DLG_IMG_WARNING
885 *
886 * They are now coming directly from DWT, see ImageRegistry. For backwards
887 * compatibility they are still supported, however new code should use DWT
888 * for these.
889 *
890 * @see Display#getSystemImage(int)
891 */
892 public static Image getImage(String key) {
893 return JFaceResources.getImageRegistry().get(key);
894 }
895
896 /**
897 * Returns the button created when <code>createButton</code> is called
898 * with an ID of <code>IDialogConstants.OK_ID</code>. If
899 * <code>createButton</code> was never called with this parameter, or if
900 * <code>createButton</code> is overridden, <code>getOKButton</code>
901 * will return <code>null</code>.
902 *
903 * @return the OK button or <code>null</code>
904 *
905 * @see #createButton(Composite, int, String, bool)
906 * @since 2.0
907 * @deprecated Use <code>getButton(IDialogConstants.OK_ID)</code> instead.
908 * This method will be removed soon.
909 */
910 protected Button getOKButton() {
911 return getButton(IDialogConstants.OK_ID);
912 }
913
914 /**
915 * Initializes the computation of horizontal and vertical dialog units based
916 * on the size of current font.
917 * <p>
918 * This method must be called before any of the dialog unit based conversion
919 * methods are called.
920 * </p>
921 *
922 * @param control
923 * a control from which to obtain the current font
924 */
925 protected void initializeDialogUnits(Control control) {
926 // Compute and store a font metric
927 GC gc = new GC(control);
928 gc.setFont(JFaceResources.getDialogFont());
929 fontMetrics = gc.getFontMetrics();
930 gc.dispose();
931 }
932
933 /**
934 * Notifies that the ok button of this dialog has been pressed.
935 * <p>
936 * The <code>Dialog</code> implementation of this framework method sets
937 * this dialog's return code to <code>Window.OK</code> and closes the
938 * dialog. Subclasses may override.
939 * </p>
940 */
941 protected void okPressed() {
942 setReturnCode(OK);
943 close();
944 }
945
946 /**
947 * Set the layout data of the button to a GridData with appropriate heights
948 * and widths.
949 *
950 * @param button
951 */
952 protected void setButtonLayoutData(Button button) {
953 GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
954 int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
955 Point minSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
956 data.widthHint = Math.max(widthHint, minSize.x);
957 button.setLayoutData(data);
958 }
959
960 /**
961 * Set the layout data of the button to a FormData with appropriate heights
962 * and widths.
963 *
964 * @param button
965 */
966 protected void setButtonLayoutFormData(Button button) {
967 FormData data = new FormData();
968 int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
969 Point minSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
970 data.width = Math.max(widthHint, minSize.x);
971 button.setLayoutData(data);
972 }
973
974 /**
975 * @see dwtx.jface.window.Window#close()
976 */
977 public bool close() {
978 if (getShell() !is null && !getShell().isDisposed()) {
979 saveDialogBounds(getShell());
980 }
981
982 bool returnValue = super.close();
983 if (returnValue) {
984 buttons = null;
985 buttonBar = null;
986 dialogArea = null;
987 }
988 return returnValue;
989 }
990
991 /**
992 * Applies the dialog font to all controls that currently have the default
993 * font.
994 *
995 * @param control
996 * the control to apply the font to. Font will also be applied to
997 * its children. If the control is <code>null</code> nothing
998 * happens.
999 */
1000 public static void applyDialogFont(Control control) {
1001 if (control is null || dialogFontIsDefault()) {
1002 return;
1003 }
1004 Font dialogFont = JFaceResources.getDialogFont();
1005 applyDialogFont(control, dialogFont);
1006 }
1007
1008 /**
1009 * Sets the dialog font on the control and any of its children if thier font
1010 * is not otherwise set.
1011 *
1012 * @param control
1013 * the control to apply the font to. Font will also be applied to
1014 * its children.
1015 * @param dialogFont
1016 * the dialog font to set
1017 */
1018 private static void applyDialogFont(Control control, Font dialogFont) {
1019 if (hasDefaultFont(control)) {
1020 control.setFont(dialogFont);
1021 }
1022 if ( auto comp = cast(Composite)control ) {
1023 Control[] children = comp.getChildren();
1024 for (int i = 0; i < children.length; i++) {
1025 applyDialogFont(children[i], dialogFont);
1026 }
1027 }
1028 }
1029
1030 /**
1031 * Return whether or not this control has the same font as it's default.
1032 *
1033 * @param control
1034 * Control
1035 * @return bool
1036 */
1037 private static bool hasDefaultFont(Control control) {
1038 FontData[] controlFontData = control.getFont().getFontData();
1039 FontData[] defaultFontData = getDefaultFont(control).getFontData();
1040 if (controlFontData.length is defaultFontData.length) {
1041 for (int i = 0; i < controlFontData.length; i++) {
1042 if (controlFontData[i].opEquals(defaultFontData[i])) {
1043 continue;
1044 }
1045 return false;
1046 }
1047 return true;
1048 }
1049 return false;
1050 }
1051
1052 /**
1053 * Get the default font for this type of control.
1054 *
1055 * @param control
1056 * @return the default font
1057 */
1058 private static Font getDefaultFont(Control control) {
1059 String fontName = "DEFAULT_FONT_" ~ control.classinfo.name; //$NON-NLS-1$
1060 if (JFaceResources.getFontRegistry().hasValueFor(fontName)) {
1061 return JFaceResources.getFontRegistry().get(fontName);
1062 }
1063 Font cached = control.getFont();
1064 control.setFont(null);
1065 Font defaultFont = control.getFont();
1066 control.setFont(cached);
1067 JFaceResources.getFontRegistry().put(fontName,
1068 defaultFont.getFontData());
1069 return defaultFont;
1070 }
1071
1072 /**
1073 * Return whether or not the dialog font is currently the same as the
1074 * default font.
1075 *
1076 * @return bool if the two are the same
1077 */
1078 protected static bool dialogFontIsDefault() {
1079 FontData[] dialogFontData = JFaceResources.getFontRegistry()
1080 .getFontData(JFaceResources.DIALOG_FONT);
1081 FontData[] defaultFontData = JFaceResources.getFontRegistry()
1082 .getFontData(JFaceResources.DEFAULT_FONT);
1083 return ArrayEquals(dialogFontData, defaultFontData);
1084 }
1085
1086 /*
1087 * (non-Javadoc)
1088 *
1089 * @see dwtx.jface.window.Window#create()
1090 */
1091 public void create() {
1092 super.create();
1093 applyDialogFont(buttonBar);
1094 }
1095
1096 /**
1097 * Get the IDialogBlockedHandler to be used by WizardDialogs and
1098 * ModalContexts.
1099 *
1100 * @return Returns the blockedHandler.
1101 */
1102 public static IDialogBlockedHandler getBlockedHandler() {
1103 return blockedHandler;
1104 }
1105
1106 /**
1107 * Set the IDialogBlockedHandler to be used by WizardDialogs and
1108 * ModalContexts.
1109 *
1110 * @param blockedHandler
1111 * The blockedHandler for the dialogs.
1112 */
1113 public static void setBlockedHandler(IDialogBlockedHandler blockedHandler) {
1114 Dialog.blockedHandler = blockedHandler;
1115 }
1116
1117 /**
1118 * Gets the dialog settings that should be used for remembering the bounds of
1119 * of the dialog, according to the dialog bounds strategy.
1120 *
1121 * @return settings the dialog settings used to store the dialog's location
1122 * and/or size, or <code>null</code> if the dialog's bounds should
1123 * never be stored.
1124 *
1125 * @since 3.2
1126 * @see Dialog#getDialogBoundsStrategy()
1127 */
1128 protected IDialogSettings getDialogBoundsSettings() {
1129 return null;
1130 }
1131
1132 /**
1133 * Get the integer constant that describes the strategy for persisting the
1134 * dialog bounds. This strategy is ignored if the implementer does not also
1135 * specify the dialog settings for storing the bounds in
1136 * Dialog.getDialogBoundsSettings().
1137 *
1138 * @return the constant describing the strategy for persisting the dialog
1139 * bounds.
1140 *
1141 * @since 3.2
1142 * @see Dialog#DIALOG_PERSISTLOCATION
1143 * @see Dialog#DIALOG_PERSISTSIZE
1144 * @see Dialog#getDialogBoundsSettings()
1145 */
1146 protected int getDialogBoundsStrategy() {
1147 return DIALOG_PERSISTLOCATION | DIALOG_PERSISTSIZE;
1148 }
1149
1150 /**
1151 * Saves the bounds of the shell in the appropriate dialog settings. The
1152 * bounds are recorded relative to the parent shell, if there is one, or
1153 * display coordinates if there is no parent shell.
1154 *
1155 * @param shell
1156 * The shell whose bounds are to be stored
1157 *
1158 * @since 3.2
1159 */
1160 private void saveDialogBounds(Shell shell) {
1161 IDialogSettings settings = getDialogBoundsSettings();
1162 if (settings !is null) {
1163 Point shellLocation = shell.getLocation();
1164 Point shellSize = shell.getSize();
1165 Shell parent = getParentShell();
1166 if (parent !is null) {
1167 Point parentLocation = parent.getLocation();
1168 shellLocation.x -= parentLocation.x;
1169 shellLocation.y -= parentLocation.y;
1170 }
1171 int strategy = getDialogBoundsStrategy();
1172 if ((strategy & DIALOG_PERSISTLOCATION) !is 0) {
1173 settings.put(DIALOG_ORIGIN_X, shellLocation.x);
1174 settings.put(DIALOG_ORIGIN_Y, shellLocation.y);
1175 }
1176 if ((strategy & DIALOG_PERSISTSIZE) !is 0) {
1177 settings.put(DIALOG_WIDTH, shellSize.x);
1178 settings.put(DIALOG_HEIGHT, shellSize.y);
1179 FontData [] fontDatas = JFaceResources.getDialogFont().getFontData();
1180 if (fontDatas.length > 0) {
1181 settings.put(DIALOG_FONT_DATA, fontDatas[0].toString());
1182 }
1183 }
1184 }
1185 }
1186
1187 /**
1188 * Returns the initial size to use for the shell. Overridden
1189 * to check whether a size has been stored in dialog settings.
1190 * If a size has been stored, it is returned.
1191 *
1192 * @return the initial size of the shell
1193 *
1194 * @since 3.2
1195 * @see #getDialogBoundsSettings()
1196 * @see #getDialogBoundsStrategy()
1197 */
1198 protected Point getInitialSize() {
1199 Point result = super.getInitialSize();
1200
1201 // Check the dialog settings for a stored size.
1202 if ((getDialogBoundsStrategy() & DIALOG_PERSISTSIZE)!is 0) {
1203 IDialogSettings settings = getDialogBoundsSettings();
1204 if (settings !is null) {
1205 // Check that the dialog font matches the font used
1206 // when the bounds was stored. If the font has changed,
1207 // we do not honor the stored settings.
1208 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132821
1209 bool useStoredBounds = true;
1210 String previousDialogFontData = settings.get(DIALOG_FONT_DATA);
1211 // There is a previously stored font, so we will check it.
1212 // Note that if we haven't stored the font before, then we will
1213 // use the stored bounds. This allows restoring of dialog bounds
1214 // that were stored before we started storing the fontdata.
1215 if (previousDialogFontData !is null && previousDialogFontData.length > 0) {
1216 FontData [] fontDatas = JFaceResources.getDialogFont().getFontData();
1217 if (fontDatas.length > 0) {
1218 String currentDialogFontData = fontDatas[0].toString();
1219 useStoredBounds = currentDialogFontData.equalsIgnoreCase(previousDialogFontData);
1220 }
1221 }
1222 if (useStoredBounds) {
1223 try {
1224 // Get the stored width and height.
1225 int width = settings.getInt(DIALOG_WIDTH);
1226 if (width !is DIALOG_DEFAULT_BOUNDS) {
1227 result.x = width;
1228 }
1229 int height = settings.getInt(DIALOG_HEIGHT);
1230 if (height !is DIALOG_DEFAULT_BOUNDS) {
1231 result.y = height;
1232 }
1233
1234 } catch (NumberFormatException e) {
1235 }
1236 }
1237 }
1238 }
1239 // No attempt is made to constrain the bounds. The default
1240 // constraining behavior in Window will be used.
1241 return result;
1242 }
1243
1244 /**
1245 * Returns the initial location to use for the shell. Overridden
1246 * to check whether the bounds of the dialog have been stored in
1247 * dialog settings. If a location has been stored, it is returned.
1248 *
1249 * @param initialSize
1250 * the initial size of the shell, as returned by
1251 * <code>getInitialSize</code>.
1252 * @return the initial location of the shell
1253 *
1254 * @since 3.2
1255 * @see #getDialogBoundsSettings()
1256 * @see #getDialogBoundsStrategy()
1257 */
1258 protected Point getInitialLocation(Point initialSize) {
1259 Point result = super.getInitialLocation(initialSize);
1260 if ((getDialogBoundsStrategy() & DIALOG_PERSISTLOCATION)!is 0) {
1261 IDialogSettings settings = getDialogBoundsSettings();
1262 if (settings !is null) {
1263 try {
1264 int x = settings.getInt(DIALOG_ORIGIN_X);
1265 int y = settings.getInt(DIALOG_ORIGIN_Y);
1266 result = new Point(x, y);
1267 // The coordinates were stored relative to the parent shell.
1268 // Convert to display coordinates.
1269 Shell parent = getParentShell();
1270 if (parent !is null) {
1271 Point parentLocation = parent.getLocation();
1272 result.x += parentLocation.x;
1273 result.y += parentLocation.y;
1274 }
1275 } catch (NumberFormatException e) {
1276 }
1277 }
1278 }
1279 // No attempt is made to constrain the bounds. The default
1280 // constraining behavior in Window will be used.
1281 return result;
1282 }
1283
1284 }