# HG changeset patch # User Frank Benoit # Date 1206750327 -3600 # Node ID a3ff22a98bef858d03e31c2dd0784a3125010f37 # Parent 8a302fdb4140afc19a8a00aa94271e89d0f9263c Dialog diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/dialogs/Dialog.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/dialogs/Dialog.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,1284 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.dialogs.Dialog; + +import dwtx.jface.dialogs.IDialogBlockedHandler; +import dwtx.jface.dialogs.IDialogSettings; +import dwtx.jface.dialogs.IDialogConstants; +// import tango.util.Arrays; + +import dwt.DWT; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.graphics.Font; +import dwt.graphics.FontData; +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.layout.FormData; +import dwt.layout.GridData; +import dwt.layout.GridLayout; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Shell; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.Status; +import dwtx.jface.resource.JFaceResources; +import dwtx.jface.util.Policy; +import dwtx.jface.window.IShellProvider; +import dwtx.jface.window.SameShellProvider; +import dwtx.jface.window.Window; + +import dwt.dwthelper.utils; + +/** + * A dialog is a specialized window used for narrow-focused communication with + * the user. + *

+ * Dialogs are usually modal. Consequently, it is generally bad practice to open + * a dialog without a parent. A modal dialog without a parent is not prevented + * from disappearing behind the application's other windows, making it very + * confusing for the user. + *

+ *

+ * If there is more than one modal dialog is open the second one should be + * parented off of the shell of the first one otherwise it is possible that the + * OS will give focus to the first dialog potentially blocking the UI. + *

+ */ +public abstract class Dialog : Window { + /** + * Image registry key for error image (value + * "dialog_error_image"). + * + * @deprecated use + * dwt.widgets.Display.getSystemImage(DWT.ICON_ERROR) + */ + public static const String DLG_IMG_ERROR = "dialog_error_image"; //$NON-NLS-1$ + + /** + * Image registry key for info image (value "dialog_info_image"). + * + * @deprecated use + * dwt.widgets.Display.getSystemImage(DWT.ICON_INFORMATION) + */ + public static const String DLG_IMG_INFO = "dialog_info_imageg"; //$NON-NLS-1$ + + /** + * Image registry key for question image (value + * "dialog_question_image"). + * + * @deprecated dwt.widgets.Display.getSystemImage(DWT.ICON_QUESTION) + */ + public static const String DLG_IMG_QUESTION = "dialog_question_image"; //$NON-NLS-1$ + + /** + * Image registry key for warning image (value + * "dialog_warning_image"). + * + * @deprecated use + * dwt.widgets.Display.getSystemImage(DWT.ICON_WARNING) + */ + public static const String DLG_IMG_WARNING = "dialog_warning_image"; //$NON-NLS-1$ + + /** + * Image registry key for info message image (value + * "dialog_messasge_info_image"). + * + * @since 2.0 + */ + public static const String DLG_IMG_MESSAGE_INFO = "dialog_messasge_info_image"; //$NON-NLS-1$ + + /** + * Image registry key for info message image (value + * "dialog_messasge_warning_image"). + * + * @since 2.0 + */ + public static const String DLG_IMG_MESSAGE_WARNING = "dialog_messasge_warning_image"; //$NON-NLS-1$ + + /** + * Image registry key for info message image (value + * "dialog_message_error_image"). + * + * @since 2.0 + */ + public static const String DLG_IMG_MESSAGE_ERROR = "dialog_message_error_image"; //$NON-NLS-1$ + + /** + * Image registry key for help image (value + * "dialog_help_image"). + * + * @since 3.2 + */ + public static const String DLG_IMG_HELP = "dialog_help_image"; //$NON-NLS-1$ + + /** + * The ellipsis is the string that is used to represent shortened text. + * + * @since 3.0 + */ + public static const String ELLIPSIS = "..."; //$NON-NLS-1$ + + /** + * The dialog settings key name for stored dialog x location. + * + * @since 3.2 + */ + private static const String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$ + + /** + * The dialog settings key name for stored dialog y location. + * + * @since 3.2 + */ + private static const String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$ + + /** + * The dialog settings key name for stored dialog width. + * + * @since 3.2 + */ + private static const String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$ + + /** + * The dialog settings key name for stored dialog height. + * + * @since 3.2 + */ + private static const String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$ + + /** + * The dialog settings key name for the font used when the dialog + * height and width was stored. + * + *@since 3.2 + */ + private static const String DIALOG_FONT_DATA = "DIALOG_FONT_NAME"; //$NON-NLS-1$ + + /** + * A value that can be used for stored dialog width or height that + * indicates that the default bounds should be used. + * + * @since 3.2 + */ + public static const int DIALOG_DEFAULT_BOUNDS = -1; + + /** + * Constants that can be used for specifying the strategy for persisting + * dialog bounds. These constants represent bit masks that can be used + * together. + * + *@since 3.2 + */ + + /** + * Persist the last location of the dialog. + * @since 3.2 + */ + public static const int DIALOG_PERSISTLOCATION = 0x0001; + /** + * Persist the last known size of the dialog. + * @since 3.2 + */ + public static const int DIALOG_PERSISTSIZE = 0x0002; + + /** + * The dialog area; null until dialog is layed out. + */ + protected Control dialogArea; + + /** + * The button bar; null until dialog is layed out. + */ + public Control buttonBar; + + /** + * Collection of buttons created by the createButton method. + */ + private Button[int] buttons; + + /** + * Font metrics to use for determining pixel sizes. + */ + private FontMetrics fontMetrics; + + /** + * Number of horizontal dialog units per character, value 4. + */ + private static const int HORIZONTAL_DIALOG_UNIT_PER_CHAR = 4; + + /** + * Number of vertical dialog units per character, value 8. + */ + private static const int VERTICAL_DIALOG_UNITS_PER_CHAR = 8; + + /** + * Returns the number of pixels corresponding to the height of the given + * number of characters. + *

+ * The required FontMetrics parameter may be created in the + * following way: + * GC gc = new GC(control); + * gc.setFont(control.getFont()); + * fontMetrics = gc.getFontMetrics(); + * gc.dispose(); + * + *

+ * + * @param fontMetrics + * used in performing the conversion + * @param chars + * the number of characters + * @return the number of pixels + * @since 2.0 + */ + public static int convertHeightInCharsToPixels(FontMetrics fontMetrics, + int chars) { + return fontMetrics.getHeight() * chars; + } + + /** + * Returns the number of pixels corresponding to the given number of + * horizontal dialog units. + *

+ * The required FontMetrics parameter may be created in the + * following way: + * GC gc = new GC(control); + * gc.setFont(control.getFont()); + * fontMetrics = gc.getFontMetrics(); + * gc.dispose(); + * + *

+ * + * @param fontMetrics + * used in performing the conversion + * @param dlus + * the number of horizontal dialog units + * @return the number of pixels + * @since 2.0 + */ + public static int convertHorizontalDLUsToPixels(FontMetrics fontMetrics, + int dlus) { + // round to the nearest pixel + return (fontMetrics.getAverageCharWidth() * dlus + HORIZONTAL_DIALOG_UNIT_PER_CHAR / 2) + / HORIZONTAL_DIALOG_UNIT_PER_CHAR; + } + + /** + * Returns the number of pixels corresponding to the given number of + * vertical dialog units. + *

+ * The required FontMetrics parameter may be created in the + * following way: + * GC gc = new GC(control); + * gc.setFont(control.getFont()); + * fontMetrics = gc.getFontMetrics(); + * gc.dispose(); + * + *

+ * + * @param fontMetrics + * used in performing the conversion + * @param dlus + * the number of vertical dialog units + * @return the number of pixels + * @since 2.0 + */ + public static int convertVerticalDLUsToPixels(FontMetrics fontMetrics, + int dlus) { + // round to the nearest pixel + return (fontMetrics.getHeight() * dlus + VERTICAL_DIALOG_UNITS_PER_CHAR / 2) + / VERTICAL_DIALOG_UNITS_PER_CHAR; + } + + /** + * Returns the number of pixels corresponding to the width of the given + * number of characters. + *

+ * The required FontMetrics parameter may be created in the + * following way: + * GC gc = new GC(control); + * gc.setFont(control.getFont()); + * fontMetrics = gc.getFontMetrics(); + * gc.dispose(); + * + *

+ * + * @param fontMetrics + * used in performing the conversion + * @param chars + * the number of characters + * @return the number of pixels + * @since 2.0 + */ + public static int convertWidthInCharsToPixels(FontMetrics fontMetrics, + int chars) { + return fontMetrics.getAverageCharWidth() * chars; + } + + /** + * Shortens the given text textValue so that its width in + * pixels does not exceed the width of the given control. Overrides + * characters in the center of the original string with an ellipsis ("...") + * if necessary. If a null value is given, null + * is returned. + * + * @param textValue + * the original string or null + * @param control + * the control the string will be displayed on + * @return the string to display, or null if null was passed + * in + * + * @since 3.0 + */ + public static String shortenText(String textValue, Control control) { + if (textValue is null) { + return null; + } + GC gc = new GC(control); + int maxWidth = control.getBounds().width - 5; + int maxExtent = gc.textExtent(textValue).x; + if (maxExtent < maxWidth) { + gc.dispose(); + return textValue; + } + int length = textValue.length; + int charsToClip = cast(int) Math.round(0.95f*length * (1 - (cast(float)maxWidth/maxExtent))); + int pivot = length / 2; + int start = pivot - (charsToClip/2); + int end = pivot + (charsToClip/2) + 1; + while (start >= 0 && end < length) { + String s1 = textValue.substring(0, start); + String s2 = textValue.substring(end, length); + String s = s1 ~ ELLIPSIS ~ s2; + int l = gc.textExtent(s).x; + if (l < maxWidth) { + gc.dispose(); + return s; + } + start--; + end++; + } + gc.dispose(); + return textValue; + } + + /** + * Create a default instance of the blocked handler which does not do + * anything. + */ + public static IDialogBlockedHandler blockedHandler; + static this(){ + blockedHandler = new class IDialogBlockedHandler { + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.IDialogBlockedHandler#clearBlocked() + */ + public void clearBlocked() { + // No default behaviour + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.IDialogBlockedHandler#showBlocked(dwtx.core.runtime.IProgressMonitor, + * dwtx.core.runtime.IStatus, java.lang.String) + */ + public void showBlocked(IProgressMonitor blocking, + IStatus blockingStatus, String blockedName) { + // No default behaviour + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.IDialogBlockedHandler#showBlocked(dwt.widgets.Shell, + * dwtx.core.runtime.IProgressMonitor, + * dwtx.core.runtime.IStatus, java.lang.String) + */ + public void showBlocked(Shell parentShell, IProgressMonitor blocking, + IStatus blockingStatus, String blockedName) { + // No default behaviour + } + }; + } + + /** + * Creates a dialog instance. Note that the window will have no visual + * representation (no widgets) until it is told to open. By default, + * open blocks for dialogs. + * + * @param parentShell + * the parent shell, or null to create a top-level + * shell + */ + protected this(Shell parentShell) { + this(new SameShellProvider(parentShell)); + if (parentShell is null && Policy.DEBUG_DIALOG_NO_PARENT) { + Policy.getLog().log( + new Status(IStatus.INFO, Policy.JFACE, IStatus.INFO, this + .classinfo.name + ~ " created with no shell",//$NON-NLS-1$ + new Exception( null, null ))); + } + } + + /** + * Creates a dialog with the given parent. + * + * @param parentShell + * object that returns the current parent shell + * + * @since 3.1 + */ + protected this(IShellProvider parentShell) { + super(parentShell); + setShellStyle(DWT.DIALOG_TRIM | DWT.APPLICATION_MODAL + | getDefaultOrientation()); + setBlockOnOpen(true); + } + + /** + * Notifies that this dialog's button with the given id has been pressed. + *

+ * The Dialog implementation of this framework method calls + * okPressed if the ok button is the pressed, and + * cancelPressed if the cancel button is the pressed. All + * other button presses are ignored. Subclasses may override to handle other + * buttons, but should call super.buttonPressed if the + * default handling of the ok and cancel buttons is desired. + *

+ * + * @param buttonId + * the id of the button that was pressed (see + * IDialogConstants.*_ID constants) + */ + protected void buttonPressed(int buttonId) { + if (IDialogConstants.OK_ID is buttonId) { + okPressed(); + } else if (IDialogConstants.CANCEL_ID is buttonId) { + cancelPressed(); + } + } + + /** + * Notifies that the cancel button of this dialog has been pressed. + *

+ * The Dialog implementation of this framework method sets + * this dialog's return code to Window.CANCEL and closes the + * dialog. Subclasses may override if desired. + *

+ */ + protected void cancelPressed() { + setReturnCode(CANCEL); + close(); + } + + /** + * Returns the number of pixels corresponding to the height of the given + * number of characters. + *

+ * This method may only be called after initializeDialogUnits + * has been called. + *

+ *

+ * Clients may call this framework method, but should not override it. + *

+ * + * @param chars + * the number of characters + * @return the number of pixels + */ + protected int convertHeightInCharsToPixels(int chars) { + // test for failure to initialize for backward compatibility + if (fontMetrics is null) { + return 0; + } + return convertHeightInCharsToPixels(fontMetrics, chars); + } + + /** + * Returns the number of pixels corresponding to the given number of + * horizontal dialog units. + *

+ * This method may only be called after initializeDialogUnits + * has been called. + *

+ *

+ * Clients may call this framework method, but should not override it. + *

+ * + * @param dlus + * the number of horizontal dialog units + * @return the number of pixels + */ + protected int convertHorizontalDLUsToPixels(int dlus) { + // test for failure to initialize for backward compatibility + if (fontMetrics is null) { + return 0; + } + return convertHorizontalDLUsToPixels(fontMetrics, dlus); + } + + /** + * Returns the number of pixels corresponding to the given number of + * vertical dialog units. + *

+ * This method may only be called after initializeDialogUnits + * has been called. + *

+ *

+ * Clients may call this framework method, but should not override it. + *

+ * + * @param dlus + * the number of vertical dialog units + * @return the number of pixels + */ + protected int convertVerticalDLUsToPixels(int dlus) { + // test for failure to initialize for backward compatibility + if (fontMetrics is null) { + return 0; + } + return convertVerticalDLUsToPixels(fontMetrics, dlus); + } + + /** + * Returns the number of pixels corresponding to the width of the given + * number of characters. + *

+ * This method may only be called after initializeDialogUnits + * has been called. + *

+ *

+ * Clients may call this framework method, but should not override it. + *

+ * + * @param chars + * the number of characters + * @return the number of pixels + */ + protected int convertWidthInCharsToPixels(int chars) { + // test for failure to initialize for backward compatibility + if (fontMetrics is null) { + return 0; + } + return convertWidthInCharsToPixels(fontMetrics, chars); + } + + /** + * Creates a new button with the given id. + *

+ * The Dialog implementation of this framework method creates + * a standard push button, registers it for selection events including + * button presses, and registers default buttons with its shell. The button + * id is stored as the button's client data. If the button id is + * IDialogConstants.CANCEL_ID, the new button will be + * accessible from getCancelButton(). If the button id is + * IDialogConstants.OK_ID, the new button will be accesible + * from getOKButton(). Note that the parent's layout is + * assumed to be a GridLayout and the number of columns in + * this layout is incremented. Subclasses may override. + *

+ * + * @param parent + * the parent composite + * @param id + * the id of the button (see IDialogConstants.*_ID + * constants for standard dialog button ids) + * @param label + * the label from the button + * @param defaultButton + * true if the button is to be the default button, + * and false otherwise + * + * @return the new button + * + * @see #getCancelButton + * @see #getOKButton() + */ + protected Button createButton(Composite parent, int id, String label, + bool defaultButton) { + // increment the number of columns in the button bar + (cast(GridLayout) parent.getLayout()).numColumns++; + Button button = new Button(parent, DWT.PUSH); + button.setText(label); + button.setFont(JFaceResources.getDialogFont()); + button.setData(new ValueWrapperInt(id)); + button.addSelectionListener(new class SelectionAdapter { + public void widgetSelected(SelectionEvent event) { + buttonPressed((cast(ValueWrapperInt) event.widget.getData()).value); + } + }); + if (defaultButton) { + Shell shell = parent.getShell(); + if (shell !is null) { + shell.setDefaultButton(button); + } + } + buttons[id] = button; + setButtonLayoutData(button); + return button; + } + + /** + * Creates and returns the contents of this dialog's button bar. + *

+ * The Dialog implementation of this framework method lays + * out a button bar and calls the createButtonsForButtonBar + * framework method to populate it. Subclasses may override. + *

+ *

+ * The returned control's layout data must be an instance of + * GridData. + *

+ * + * @param parent + * the parent composite to contain the button bar + * @return the button bar control + */ + protected Control createButtonBar(Composite parent) { + Composite composite = new Composite(parent, DWT.NONE); + // create a layout with spacing and margins appropriate for the font + // size. + GridLayout layout = new GridLayout(); + layout.numColumns = 0; // this is incremented by createButton + layout.makeColumnsEqualWidth = true; + layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + composite.setLayout(layout); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END + | GridData.VERTICAL_ALIGN_CENTER); + composite.setLayoutData(data); + composite.setFont(parent.getFont()); + + // Add the buttons to the button bar. + createButtonsForButtonBar(composite); + return composite; + } + + /** + * Adds buttons to this dialog's button bar. + *

+ * The Dialog implementation of this framework method adds + * standard ok and cancel buttons using the createButton + * framework method. These standard buttons will be accessible from + * getCancelButton, and getOKButton. + * Subclasses may override. + *

+ * + * @param parent + * the button bar composite + */ + protected void createButtonsForButtonBar(Composite parent) { + // create OK and Cancel buttons by default + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, + true); + createButton(parent, IDialogConstants.CANCEL_ID, + IDialogConstants.CANCEL_LABEL, false); + } + + /* + * @see Window.initializeBounds() + */ + protected void initializeBounds() { + String platform = DWT.getPlatform(); + if ("carbon".equals(platform)) { //$NON-NLS-1$ + // On Mac OS X the default button must be the right-most button + Shell shell = getShell(); + if (shell !is null) { + Button defaultButton = shell.getDefaultButton(); + if (defaultButton !is null + && isContained(buttonBar, defaultButton)) { + defaultButton.moveBelow(null); + } + } + } + + super.initializeBounds(); + } + + /** + * Returns true if the given Control is a direct or indirect child of + * container. + * + * @param container + * the potential parent + * @param control + * @return bool true if control is a child of container + */ + private bool isContained(Control container, Control control) { + Composite parent; + while ((parent = control.getParent()) !is null) { + if (parent is container) { + return true; + } + control = parent; + } + return false; + } + + /** + * The Dialog implementation of this Window + * method creates and lays out the top level composite for the dialog, and + * determines the appropriate horizontal and vertical dialog units based on + * the font size. It then calls the createDialogArea and + * createButtonBar methods to create the dialog area and + * button bar, respectively. Overriding createDialogArea and + * createButtonBar are recommended rather than overriding + * this method. + */ + protected Control createContents(Composite parent) { + // create the top level composite for the dialog + Composite composite = new Composite(parent, 0); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.verticalSpacing = 0; + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + applyDialogFont(composite); + // initialize the dialog units + initializeDialogUnits(composite); + // create the dialog area and button bar + dialogArea = createDialogArea(composite); + buttonBar = createButtonBar(composite); + + return composite; + } + + /** + * Creates and returns the contents of the upper part of this dialog (above + * the button bar). + *

+ * The Dialog implementation of this framework method creates + * and returns a new Composite with standard margins and + * spacing. + *

+ *

+ * The returned control's layout data must be an instance of + * GridData. This method must not modify the parent's + * layout. + *

+ *

+ * Subclasses must override this method but may call super as + * in the following example: + *

+ * + *
+     * Composite composite = (Composite) super.createDialogArea(parent);
+     * //add controls to composite as necessary
+     * return composite;
+     * 
+ * + * @param parent + * the parent composite to contain the dialog area + * @return the dialog area control + */ + protected Control createDialogArea(Composite parent) { + // create a composite with standard margins and spacing + Composite composite = new Composite(parent, DWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + applyDialogFont(composite); + return composite; + } + + /** + * Returns the button created by the method createButton for + * the specified ID as defined on IDialogConstants. If + * createButton was never called with this ID, or if + * createButton is overridden, this method will return + * null. + * + * @param id + * the id of the button to look for + * + * @return the button for the ID or null + * + * @see #createButton(Composite, int, String, bool) + * @since 2.0 + */ + protected Button getButton(int id) { + return buttons[id]; + } + + /** + * Returns the button bar control. + *

+ * Clients may call this framework method, but should not override it. + *

+ * + * @return the button bar, or null if the button bar has not + * been created yet + */ + protected Control getButtonBar() { + return buttonBar; + } + + /** + * Returns the button created when createButton is called + * with an ID of IDialogConstants.CANCEL_ID. If + * createButton was never called with this parameter, or if + * createButton is overridden, getCancelButton + * will return null. + * + * @return the cancel button or null + * + * @see #createButton(Composite, int, String, bool) + * @since 2.0 + * @deprecated Use getButton(IDialogConstants.CANCEL_ID) + * instead. This method will be removed soon. + */ + protected Button getCancelButton() { + return getButton(IDialogConstants.CANCEL_ID); + } + + /** + * Returns the dialog area control. + *

+ * Clients may call this framework method, but should not override it. + *

+ * + * @return the dialog area, or null if the dialog area has + * not been created yet + */ + protected Control getDialogArea() { + return dialogArea; + } + + /** + * Returns the standard dialog image with the given key. Note that these + * images are managed by the dialog framework, and must not be disposed by + * another party. + * + * @param key + * one of the Dialog.DLG_IMG_* constants + * @return the standard dialog image + * + * NOTE: Dialog does not use the following images in the registry + * DLG_IMG_ERROR DLG_IMG_INFO DLG_IMG_QUESTION DLG_IMG_WARNING + * + * They are now coming directly from DWT, see ImageRegistry. For backwards + * compatibility they are still supported, however new code should use DWT + * for these. + * + * @see Display#getSystemImage(int) + */ + public static Image getImage(String key) { + return JFaceResources.getImageRegistry().get(key); + } + + /** + * Returns the button created when createButton is called + * with an ID of IDialogConstants.OK_ID. If + * createButton was never called with this parameter, or if + * createButton is overridden, getOKButton + * will return null. + * + * @return the OK button or null + * + * @see #createButton(Composite, int, String, bool) + * @since 2.0 + * @deprecated Use getButton(IDialogConstants.OK_ID) instead. + * This method will be removed soon. + */ + protected Button getOKButton() { + return getButton(IDialogConstants.OK_ID); + } + + /** + * Initializes the computation of horizontal and vertical dialog units based + * on the size of current font. + *

+ * This method must be called before any of the dialog unit based conversion + * methods are called. + *

+ * + * @param control + * a control from which to obtain the current font + */ + protected void initializeDialogUnits(Control control) { + // Compute and store a font metric + GC gc = new GC(control); + gc.setFont(JFaceResources.getDialogFont()); + fontMetrics = gc.getFontMetrics(); + gc.dispose(); + } + + /** + * Notifies that the ok button of this dialog has been pressed. + *

+ * The Dialog implementation of this framework method sets + * this dialog's return code to Window.OK and closes the + * dialog. Subclasses may override. + *

+ */ + protected void okPressed() { + setReturnCode(OK); + close(); + } + + /** + * Set the layout data of the button to a GridData with appropriate heights + * and widths. + * + * @param button + */ + protected void setButtonLayoutData(Button button) { + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); + Point minSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, true); + data.widthHint = Math.max(widthHint, minSize.x); + button.setLayoutData(data); + } + + /** + * Set the layout data of the button to a FormData with appropriate heights + * and widths. + * + * @param button + */ + protected void setButtonLayoutFormData(Button button) { + FormData data = new FormData(); + int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); + Point minSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, true); + data.width = Math.max(widthHint, minSize.x); + button.setLayoutData(data); + } + + /** + * @see dwtx.jface.window.Window#close() + */ + public bool close() { + if (getShell() !is null && !getShell().isDisposed()) { + saveDialogBounds(getShell()); + } + + bool returnValue = super.close(); + if (returnValue) { + buttons = null; + buttonBar = null; + dialogArea = null; + } + return returnValue; + } + + /** + * Applies the dialog font to all controls that currently have the default + * font. + * + * @param control + * the control to apply the font to. Font will also be applied to + * its children. If the control is null nothing + * happens. + */ + public static void applyDialogFont(Control control) { + if (control is null || dialogFontIsDefault()) { + return; + } + Font dialogFont = JFaceResources.getDialogFont(); + applyDialogFont(control, dialogFont); + } + + /** + * Sets the dialog font on the control and any of its children if thier font + * is not otherwise set. + * + * @param control + * the control to apply the font to. Font will also be applied to + * its children. + * @param dialogFont + * the dialog font to set + */ + private static void applyDialogFont(Control control, Font dialogFont) { + if (hasDefaultFont(control)) { + control.setFont(dialogFont); + } + if ( auto comp = cast(Composite)control ) { + Control[] children = comp.getChildren(); + for (int i = 0; i < children.length; i++) { + applyDialogFont(children[i], dialogFont); + } + } + } + + /** + * Return whether or not this control has the same font as it's default. + * + * @param control + * Control + * @return bool + */ + private static bool hasDefaultFont(Control control) { + FontData[] controlFontData = control.getFont().getFontData(); + FontData[] defaultFontData = getDefaultFont(control).getFontData(); + if (controlFontData.length is defaultFontData.length) { + for (int i = 0; i < controlFontData.length; i++) { + if (controlFontData[i].opEquals(defaultFontData[i])) { + continue; + } + return false; + } + return true; + } + return false; + } + + /** + * Get the default font for this type of control. + * + * @param control + * @return the default font + */ + private static Font getDefaultFont(Control control) { + String fontName = "DEFAULT_FONT_" ~ control.classinfo.name; //$NON-NLS-1$ + if (JFaceResources.getFontRegistry().hasValueFor(fontName)) { + return JFaceResources.getFontRegistry().get(fontName); + } + Font cached = control.getFont(); + control.setFont(null); + Font defaultFont = control.getFont(); + control.setFont(cached); + JFaceResources.getFontRegistry().put(fontName, + defaultFont.getFontData()); + return defaultFont; + } + + /** + * Return whether or not the dialog font is currently the same as the + * default font. + * + * @return bool if the two are the same + */ + protected static bool dialogFontIsDefault() { + FontData[] dialogFontData = JFaceResources.getFontRegistry() + .getFontData(JFaceResources.DIALOG_FONT); + FontData[] defaultFontData = JFaceResources.getFontRegistry() + .getFontData(JFaceResources.DEFAULT_FONT); + return ArrayEquals(dialogFontData, defaultFontData); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#create() + */ + public void create() { + super.create(); + applyDialogFont(buttonBar); + } + + /** + * Get the IDialogBlockedHandler to be used by WizardDialogs and + * ModalContexts. + * + * @return Returns the blockedHandler. + */ + public static IDialogBlockedHandler getBlockedHandler() { + return blockedHandler; + } + + /** + * Set the IDialogBlockedHandler to be used by WizardDialogs and + * ModalContexts. + * + * @param blockedHandler + * The blockedHandler for the dialogs. + */ + public static void setBlockedHandler(IDialogBlockedHandler blockedHandler) { + Dialog.blockedHandler = blockedHandler; + } + + /** + * Gets the dialog settings that should be used for remembering the bounds of + * of the dialog, according to the dialog bounds strategy. + * + * @return settings the dialog settings used to store the dialog's location + * and/or size, or null if the dialog's bounds should + * never be stored. + * + * @since 3.2 + * @see Dialog#getDialogBoundsStrategy() + */ + protected IDialogSettings getDialogBoundsSettings() { + return null; + } + + /** + * Get the integer constant that describes the strategy for persisting the + * dialog bounds. This strategy is ignored if the implementer does not also + * specify the dialog settings for storing the bounds in + * Dialog.getDialogBoundsSettings(). + * + * @return the constant describing the strategy for persisting the dialog + * bounds. + * + * @since 3.2 + * @see Dialog#DIALOG_PERSISTLOCATION + * @see Dialog#DIALOG_PERSISTSIZE + * @see Dialog#getDialogBoundsSettings() + */ + protected int getDialogBoundsStrategy() { + return DIALOG_PERSISTLOCATION | DIALOG_PERSISTSIZE; + } + + /** + * Saves the bounds of the shell in the appropriate dialog settings. The + * bounds are recorded relative to the parent shell, if there is one, or + * display coordinates if there is no parent shell. + * + * @param shell + * The shell whose bounds are to be stored + * + * @since 3.2 + */ + private void saveDialogBounds(Shell shell) { + IDialogSettings settings = getDialogBoundsSettings(); + if (settings !is null) { + Point shellLocation = shell.getLocation(); + Point shellSize = shell.getSize(); + Shell parent = getParentShell(); + if (parent !is null) { + Point parentLocation = parent.getLocation(); + shellLocation.x -= parentLocation.x; + shellLocation.y -= parentLocation.y; + } + int strategy = getDialogBoundsStrategy(); + if ((strategy & DIALOG_PERSISTLOCATION) !is 0) { + settings.put(DIALOG_ORIGIN_X, shellLocation.x); + settings.put(DIALOG_ORIGIN_Y, shellLocation.y); + } + if ((strategy & DIALOG_PERSISTSIZE) !is 0) { + settings.put(DIALOG_WIDTH, shellSize.x); + settings.put(DIALOG_HEIGHT, shellSize.y); + FontData [] fontDatas = JFaceResources.getDialogFont().getFontData(); + if (fontDatas.length > 0) { + settings.put(DIALOG_FONT_DATA, fontDatas[0].toString()); + } + } + } + } + + /** + * Returns the initial size to use for the shell. Overridden + * to check whether a size has been stored in dialog settings. + * If a size has been stored, it is returned. + * + * @return the initial size of the shell + * + * @since 3.2 + * @see #getDialogBoundsSettings() + * @see #getDialogBoundsStrategy() + */ + protected Point getInitialSize() { + Point result = super.getInitialSize(); + + // Check the dialog settings for a stored size. + if ((getDialogBoundsStrategy() & DIALOG_PERSISTSIZE)!is 0) { + IDialogSettings settings = getDialogBoundsSettings(); + if (settings !is null) { + // Check that the dialog font matches the font used + // when the bounds was stored. If the font has changed, + // we do not honor the stored settings. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132821 + bool useStoredBounds = true; + String previousDialogFontData = settings.get(DIALOG_FONT_DATA); + // There is a previously stored font, so we will check it. + // Note that if we haven't stored the font before, then we will + // use the stored bounds. This allows restoring of dialog bounds + // that were stored before we started storing the fontdata. + if (previousDialogFontData !is null && previousDialogFontData.length > 0) { + FontData [] fontDatas = JFaceResources.getDialogFont().getFontData(); + if (fontDatas.length > 0) { + String currentDialogFontData = fontDatas[0].toString(); + useStoredBounds = currentDialogFontData.equalsIgnoreCase(previousDialogFontData); + } + } + if (useStoredBounds) { + try { + // Get the stored width and height. + int width = settings.getInt(DIALOG_WIDTH); + if (width !is DIALOG_DEFAULT_BOUNDS) { + result.x = width; + } + int height = settings.getInt(DIALOG_HEIGHT); + if (height !is DIALOG_DEFAULT_BOUNDS) { + result.y = height; + } + + } catch (NumberFormatException e) { + } + } + } + } + // No attempt is made to constrain the bounds. The default + // constraining behavior in Window will be used. + return result; + } + + /** + * Returns the initial location to use for the shell. Overridden + * to check whether the bounds of the dialog have been stored in + * dialog settings. If a location has been stored, it is returned. + * + * @param initialSize + * the initial size of the shell, as returned by + * getInitialSize. + * @return the initial location of the shell + * + * @since 3.2 + * @see #getDialogBoundsSettings() + * @see #getDialogBoundsStrategy() + */ + protected Point getInitialLocation(Point initialSize) { + Point result = super.getInitialLocation(initialSize); + if ((getDialogBoundsStrategy() & DIALOG_PERSISTLOCATION)!is 0) { + IDialogSettings settings = getDialogBoundsSettings(); + if (settings !is null) { + try { + int x = settings.getInt(DIALOG_ORIGIN_X); + int y = settings.getInt(DIALOG_ORIGIN_Y); + result = new Point(x, y); + // The coordinates were stored relative to the parent shell. + // Convert to display coordinates. + Shell parent = getParentShell(); + if (parent !is null) { + Point parentLocation = parent.getLocation(); + result.x += parentLocation.x; + result.y += parentLocation.y; + } + } catch (NumberFormatException e) { + } + } + } + // No attempt is made to constrain the bounds. The default + // constraining behavior in Window will be used. + return result; + } + +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/dialogs/IDialogBlockedHandler.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/dialogs/IDialogBlockedHandler.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.dialogs.IDialogBlockedHandler; + + +import dwt.widgets.Shell; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.IStatus; +import dwtx.jface.wizard.WizardDialog; + +import dwt.dwthelper.utils; + +/** + * The IDialogBlockedHandler is the handler used by + * JFace to provide extra information when a + * blocking has occured. There is one static instance + * of this class used by WizardDialog and ModalContext. + * @see dwtx.core.runtime.IProgressMonitorWithBlocking#clearBlocked() + * @see dwtx.core.runtime.IProgressMonitorWithBlocking#setBlocked(IStatus) + * @see WizardDialog + * @since 3.0 + */ +public interface IDialogBlockedHandler { + /** + * The blockage has been cleared. Clear the + * extra information and resume. + */ + public void clearBlocked(); + + /** + * A blockage has occured. Show the blockage and + * forward any actions to blockingMonitor. + * NOTE: This will open any blocked notification immediately + * even if there is a modal shell open. + * + * @param parentShell The shell this is being sent from. If the parent + * shell is null the behavior will be the same as + * IDialogBlockedHandler#showBlocked(IProgressMonitor, IStatus, String) + * + * @param blocking The monitor to forward to. This is most + * important for calls to cancel(). + * @param blockingStatus The status that describes the blockage + * @param blockedName The name of the locked operation. + * @see IDialogBlockedHandler#showBlocked(IProgressMonitor, IStatus, String) + */ + public void showBlocked(Shell parentShell, IProgressMonitor blocking, + IStatus blockingStatus, String blockedName); + + /** + * A blockage has occured. Show the blockage when there is + * no longer any modal shells in the UI and forward any actions + * to blockingMonitor. + * + * NOTE: As no shell has been specified this method will + * not open any blocked notification until all other modal shells + * have been closed. + * + * @param blocking The monitor to forward to. This is most + * important for calls to cancel(). + * @param blockingStatus The status that describes the blockage + * @param blockedName The name of the locked operation. + */ + public void showBlocked(IProgressMonitor blocking, IStatus blockingStatus, + String blockedName); +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/dialogs/IDialogConstants.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/dialogs/IDialogConstants.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,371 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.dialogs.IDialogConstants; + +/** + * Various dialog-related constants. + *

+ * Within the dialog framework, all buttons are referred to by a button id. + * Various common buttons, like "OK", "Cancel", and "Finish", have pre-assigned + * button ids for convenience. If an application requires other dialog buttons, + * they should be assigned application-specific button ids counting up from + * CLIENT_ID. + *

+ *

+ * Button label constants are also provided for the common buttons. JFace + * automatically localizes these strings to the current locale; that is, + * YES_LABEL would be bound to the string "Si" in + * a Spanish locale, but to "Oui" in a French one. + *

+ *

+ * All margins, spacings, and sizes are given in "dialog units" (DLUs), where + *

    + *
  • 1 horizontal DLU = 1/4 average character width
  • + *
  • 1 vertical DLU = 1/8 average character height
  • + *
+ *

+ */ +import dwtx.jface.resource.JFaceResources; + +import dwt.dwthelper.utils; + +/** + * IDialogConstants is the interface for common dialog strings and ids + * used throughout JFace. + * It is recommended that you use these labels and ids whereever + * for consistency with the JFace dialogs. + */ +public abstract class IDialogConstants { + static{ + const{ + // button ids + + // Note: if new button ids are added, see + // MessageDialogWithToggle.mapButtonLabelToButtonID(String, int) + /** + * Button id for an "Ok" button (value 0). + */ + public int OK_ID = 0; + + /** + * Button id for a "Cancel" button (value 1). + */ + public int CANCEL_ID = 1; + + /** + * Button id for a "Yes" button (value 2). + */ + public int YES_ID = 2; + + /** + * Button id for a "No" button (value 3). + */ + public int NO_ID = 3; + + /** + * Button id for a "Yes to All" button (value 4). + */ + public int YES_TO_ALL_ID = 4; + + /** + * Button id for a "Skip" button (value 5). + */ + public int SKIP_ID = 5; + + /** + * Button id for a "Stop" button (value 6). + */ + public int STOP_ID = 6; + + /** + * Button id for an "Abort" button (value 7). + */ + public int ABORT_ID = 7; + + /** + * Button id for a "Retry" button (value 8). + */ + public int RETRY_ID = 8; + + /** + * Button id for an "Ignore" button (value 9). + */ + public int IGNORE_ID = 9; + + /** + * Button id for a "Proceed" button (value 10). + */ + public int PROCEED_ID = 10; + + /** + * Button id for an "Open" button (value 11). + */ + public int OPEN_ID = 11; + + /** + * Button id for a "Close" button (value 12). + */ + public int CLOSE_ID = 12; + + /** + * Button id for a "Details" button (value 13). + */ + public int DETAILS_ID = 13; + + /** + * Button id for a "Back" button (value 14). + */ + public int BACK_ID = 14; + + /** + * Button id for a "Next" button (value 15). + */ + public int NEXT_ID = 15; + + /** + * Button id for a "Finish" button (value 16). + */ + public int FINISH_ID = 16; + + /** + * Button id for a "Help" button (value 17). + */ + public int HELP_ID = 17; + + /** + * Button id for a "Select All" button (value 18). + */ + public int SELECT_ALL_ID = 18; + + /** + * Button id for a "Deselect All" button (value 19). + */ + public int DESELECT_ALL_ID = 19; + + /** + * Button id for a "Select types" button (value 20). + */ + public int SELECT_TYPES_ID = 20; + + /** + * Button id for a "No to All" button (value 21). + */ + public int NO_TO_ALL_ID = 21; + + /** + * Starting button id reserved for internal use by JFace (value 256). JFace + * classes make ids by adding to this number. + */ + public int INTERNAL_ID = 256; + + /** + * Starting button id reserved for use by clients of JFace (value 1024). + * Clients of JFace should make ids by adding to this number. + */ + public int CLIENT_ID = 1024; + } // const + // button labels + /** + * The label for OK buttons. + */ + public String OK_LABEL; + + /** + * The label for cancel buttons. + */ + public String CANCEL_LABEL; + + /** + * The label for yes buttons. + */ + public String YES_LABEL; + + /** + * The label for no buttons. + */ + public String NO_LABEL; + + /** + * The label for not to all buttons. + */ + public String NO_TO_ALL_LABEL; + + /** + * The label for yes to all buttons. + */ + public String YES_TO_ALL_LABEL; + + /** + * The label for skip buttons. + */ + public String SKIP_LABEL; + + /** + * The label for stop buttons. + */ + public String STOP_LABEL; + + /** + * The label for abort buttons. + */ + public String ABORT_LABEL; + + /** + * The label for retry buttons. + */ + public String RETRY_LABEL; + + /** + * The label for ignore buttons. + */ + public String IGNORE_LABEL; + + /** + * The label for proceed buttons. + */ + public String PROCEED_LABEL; + + /** + * The label for open buttons. + */ + public String OPEN_LABEL; + + /** + * The label for close buttons. + */ + public String CLOSE_LABEL; + + /** + * The label for show details buttons. + */ + public String SHOW_DETAILS_LABEL; + + /** + * The label for hide details buttons. + */ + public String HIDE_DETAILS_LABEL; + + /** + * The label for back buttons. + */ + public String BACK_LABEL; + + /** + * The label for next buttons. + */ + public String NEXT_LABEL; + + /** + * The label for finish buttons. + */ + public String FINISH_LABEL; + + /** + * The label for help buttons. + */ + public String HELP_LABEL; + + const{ + // Margins, spacings, and sizes + /** + * Vertical margin in dialog units (value 7). + */ + public int VERTICAL_MARGIN = 7; + + /** + * Vertical spacing in dialog units (value 4). + */ + public int VERTICAL_SPACING = 4; + + /** + * Horizontal margin in dialog units (value 7). + */ + public int HORIZONTAL_MARGIN = 7; + + /** + * Horizontal spacing in dialog units (value 4). + */ + public int HORIZONTAL_SPACING = 4; + + /** + * Height of button bar in dialog units (value 25). + */ + public int BUTTON_BAR_HEIGHT = 25; + + /** + * Left margin in dialog units (value 20). + */ + public int LEFT_MARGIN = 20; + + /** + * Button margin in dialog units (value 4). + */ + public int BUTTON_MARGIN = 4; + + /** + * Button height in dialog units (value 14). + * + * @deprecated This constant is no longer in use. + * The button heights are now determined by the layout. + */ + public int BUTTON_HEIGHT = 14; + + /** + * Button width in dialog units (value 61). + */ + public int BUTTON_WIDTH = 61; + + /** + * Indent in dialog units (value 21). + */ + public int INDENT = 21; + + /** + * Small indent in dialog units (value 7). + */ + public int SMALL_INDENT = 7; + + /** + * Entry field width in dialog units (value 200). + */ + public int ENTRY_FIELD_WIDTH = 200; + + /** + * Minimum width of message area in dialog units (value 300). + */ + public int MINIMUM_MESSAGE_AREA_WIDTH = 300; + }// const + }// static +} +static this(){ + IDialogConstants.OK_LABEL = JFaceResources.getString("ok"); //$NON-NLS-1$ + IDialogConstants.CANCEL_LABEL = JFaceResources.getString("cancel"); //$NON-NLS-1$ + IDialogConstants.YES_LABEL = JFaceResources.getString("yes"); //$NON-NLS-1$ + IDialogConstants.NO_LABEL = JFaceResources.getString("no"); //$NON-NLS-1$ + IDialogConstants.NO_TO_ALL_LABEL = JFaceResources.getString("notoall"); //$NON-NLS-1$ + IDialogConstants.YES_TO_ALL_LABEL = JFaceResources.getString("yestoall"); //$NON-NLS-1$ + IDialogConstants.SKIP_LABEL = JFaceResources.getString("skip"); //$NON-NLS-1$ + IDialogConstants.STOP_LABEL = JFaceResources.getString("stop"); //$NON-NLS-1$ + IDialogConstants.ABORT_LABEL = JFaceResources.getString("abort"); //$NON-NLS-1$ + IDialogConstants.RETRY_LABEL = JFaceResources.getString("retry"); //$NON-NLS-1$ + IDialogConstants.IGNORE_LABEL = JFaceResources.getString("ignore"); //$NON-NLS-1$ + IDialogConstants.PROCEED_LABEL = JFaceResources.getString("proceed"); //$NON-NLS-1$ + IDialogConstants.OPEN_LABEL = JFaceResources.getString("open"); //$NON-NLS-1$ + IDialogConstants.CLOSE_LABEL = JFaceResources.getString("close"); //$NON-NLS-1$ + IDialogConstants.SHOW_DETAILS_LABEL = JFaceResources.getString("showDetails"); //$NON-NLS-1$ + IDialogConstants.HIDE_DETAILS_LABEL = JFaceResources.getString("hideDetails"); //$NON-NLS-1$ + IDialogConstants.BACK_LABEL = JFaceResources.getString("backButton"); //$NON-NLS-1$ + IDialogConstants.NEXT_LABEL = JFaceResources.getString("nextButton"); //$NON-NLS-1$ + IDialogConstants.FINISH_LABEL = JFaceResources.getString("finish"); //$NON-NLS-1$ + IDialogConstants.HELP_LABEL = JFaceResources.getString("help"); //$NON-NLS-1$ +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/dialogs/IDialogSettings.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/dialogs/IDialogSettings.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,270 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.dialogs.IDialogSettings; + +// import java.io.IOException; +// import java.io.Reader; +// import java.io.Writer; + +import dwt.dwthelper.utils; + +/** + * An interface to a storage mechanism for making dialog settings persistent. + * The store manages a collection of key/value pairs. The keys must be strings + * and the values can be either, strings or array of strings. Convenience API to + * convert primitive types to strings is provided. + */ +public interface IDialogSettings { + /** + * Create a new section in the receiver and return it. + * + * @param name + * the name of the new section + * @return the new section + */ + public IDialogSettings addNewSection(String name); + + /** + * Add a section in the receiver. + * + * @param section + * the section to be added + */ + public void addSection(IDialogSettings section); + + /** + * Returns the value of the given key in this dialog settings. + * + * @param key + * the key + * @return the value, or null if none + */ + public String get(String key); + + /** + * Returns the value, an array of strings, of the given key in this dialog + * settings. + * + * @param key + * the key + * @return the array of string, or null if none + */ + public String[] getArray(String key); + + /** + * Convenience API. Convert the value of the given key in this dialog + * settings to a bool and return it. + * + * @param key + * the key + * @return the bool value, or false if none + */ + public bool getBoolean(String key); + + /** + * Convenience API. Convert the value of the given key in this dialog + * settings to a double and return it. + * + * @param key + * the key + * @return the value coverted to double, or throws + * NumberFormatException if none + * + * @exception NumberFormatException + * if the string value does not contain a parsable number. + * @see java.lang.Double#valueOf(java.lang.String) + */ + public double getDouble(String key); + + /** + * Convenience API. Convert the value of the given key in this dialog + * settings to a float and return it. + * + * @param key + * the key + * @return the value coverted to float, or throws + * NumberFormatException if none + * + * @exception NumberFormatException + * if the string value does not contain a parsable number. + * @see java.lang.Float#valueOf(java.lang.String) + */ + public float getFloat(String key); + + /** + * Convenience API. Convert the value of the given key in this dialog + * settings to a int and return it. + * + * @param key + * the key + * @return the value coverted to int, or throws + * NumberFormatException if none + * + * @exception NumberFormatException + * if the string value does not contain a parsable number. + * @see java.lang.Integer#valueOf(java.lang.String) + */ + public int getInt(String key); + + /** + * Convenience API. Convert the value of the given key in this dialog + * settings to a long and return it. + * + * @param key + * the key + * @return the value coverted to long, or throws + * NumberFormatException if none + * + * @exception NumberFormatException + * if the string value does not contain a parsable number. + * @see java.lang.Long#valueOf(java.lang.String) + */ + public long getLong(String key); + + /** + * Returns the IDialogSettings name. + * + * @return the name + */ + public String getName(); + + /** + * Returns the section with the given name in this dialog settings. + * + * @param sectionName + * the key + * @return IDialogSettings (the section), or null if none + */ + public IDialogSettings getSection(String sectionName); + + /** + * Returns all the sections in this dialog settings. + * + * @return the section, or null if none + */ + public IDialogSettings[] getSections(); + + /** + * Load a dialog settings from a stream and fill the receiver with its + * content. + * + * @param reader + * a Reader specifying the stream where the settings are read + * from. + * @throws IOException + */ + public void load(Reader reader); + + /** + * Load a dialog settings from a file and fill the receiver with its + * content. + * + * @param fileName + * the name of the file the settings are read from. + * @throws IOException + */ + public void load(String fileName); + + /** + * Adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, String[] value); + + /** + * Convenience API. Converts the double value to a string and + * adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, double value); + + /** + * Convenience API. Converts the float value to a string and + * adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, float value); + + /** + * Convenience API. Converts the int value to a string and + * adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, int value); + + /** + * Convenience API. Converts the long value to a string and + * adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, long value); + + /** + * Adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, String value); + + /** + * Convenience API. Converts the bool value to a string + * and adds the pair key/value to this dialog settings. + * + * @param key + * the key. + * @param value + * the value to be associated with the key + */ + public void put(String key, bool value); + + /** + * Save a dialog settings to a stream + * + * @param writer + * a Writer specifying the stream the settings are written in. + * @throws IOException + */ + public void save(Writer writer); + + /** + * Save a dialog settings to a file. + * + * @param fileName + * the name of the file the settings are written in. + * @throws IOException + */ + public void save(String fileName); +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/resource/ColorDescriptor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/resource/ColorDescriptor.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.resource.ColorDescriptor; + +import dwtx.jface.resource.DeviceResourceDescriptor; +import dwtx.jface.resource.RGBColorDescriptor; + +import dwt.graphics.Color; +import dwt.graphics.Device; +import dwt.graphics.RGB; + +import dwt.dwthelper.utils; + +/** + * Lightweight descriptor for an DWT color. Each ColorDescriptor will create a particular DWT + * Color on demand. This object will be compared so hashCode(...) and equals(...) must + * return meaningful values. + * + * @since 3.1 + */ +public abstract class ColorDescriptor : DeviceResourceDescriptor { + + /** + * Creates a ColorDescriptor from an existing Color, given the Device associated + * with the original Color. This is the usual way to convert a Color into + * a ColorDescriptor. Note that the returned ColorDescriptor depends on the + * original Color, and disposing the Color will invalidate the ColorDescriptor. + * + * @deprecated use {@link ColorDescriptor#createFrom(Color)} + * + * @since 3.1 + * + * @param toCreate Color to convert into a ColorDescriptor. + * @param originalDevice this must be the same Device that was passed into the + * original Color's constructor. + * @return a newly created ColorDescriptor that describes the given Color. + */ + public static ColorDescriptor createFrom(Color toCreate, Device originalDevice) { + return new RGBColorDescriptor(toCreate); + } + + /** + * Creates a ColorDescriptor from an existing color. + * + * The returned ColorDescriptor depends on the original Color. Disposing + * the original colour while the color descriptor is still in use may cause + * DWT to throw a graphic disposed exception. + * + * @since 3.1 + * + * @param toCreate Color to generate a ColorDescriptor from + * @return a newly created ColorDescriptor + */ + public static ColorDescriptor createFrom(Color toCreate) { + return new RGBColorDescriptor(toCreate); + } + + /** + * Returns a color descriptor for the given RGB values + * @since 3.1 + * + * @param toCreate RGB values to create + * @return a new ColorDescriptor + */ + public static ColorDescriptor createFrom(RGB toCreate) { + return new RGBColorDescriptor(toCreate); + } + + /** + * Returns the Color described by this descriptor. + * + * @param device DWT device on which to allocate the Color + * @return a newly allocated DWT Color object (never null) + * @throws DeviceResourceException if unable to allocate the Color + */ + public abstract Color createColor(Device device); + + /** + * Undoes whatever was done by createColor. + * + * @since 3.1 + * + * @param toDestroy a Color that was previously allocated by an equal ColorDescriptor + */ + public abstract void destroyColor(Color toDestroy); + + /* (non-Javadoc) + * @see dwtx.jface.resource.DeviceResourceDescriptor#createResource(dwt.graphics.Device) + */ + public final Object createResource(Device device){ + return createColor(device); + } + + /* (non-Javadoc) + * @see dwtx.jface.resource.DeviceResourceDescriptor#destroyResource(java.lang.Object) + */ + public final void destroyResource(Object previouslyCreatedObject) { + destroyColor(cast(Color)previouslyCreatedObject); + } +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/resource/ImageRegistry.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/resource/ImageRegistry.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,363 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Steven Ketcham (sketcham@dsicdi.com) - Bug 42451 + * [Dialogs] ImageRegistry throws null pointer exception in + * application with multiple Display's + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.resource.ImageRegistry; + +import dwtx.jface.resource.ImageDescriptor; +import dwtx.jface.resource.ResourceManager; +import dwtx.jface.resource.DeviceResourceException; +import dwtx.jface.resource.JFaceResources; + +import tango.util.collection.HashMap; +import tango.util.collection.model.Map; + +// import java.util.Iterator; +// import java.util.Map; + +import dwt.DWT; +import dwt.graphics.Device; +import dwt.graphics.Image; +import dwt.graphics.ImageData; +import dwt.widgets.Display; +import dwtx.core.runtime.Assert; +import dwtx.jface.dialogs.Dialog; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * An image registry maintains a mapping between symbolic image names + * and DWT image objects or special image descriptor objects which + * defer the creation of DWT image objects until they are needed. + *

+ * An image registry owns all of the image objects registered + * with it, and automatically disposes of them when the DWT Display + * that creates the images is disposed. Because of this, clients do not + * need to (indeed, must not attempt to) dispose of these images themselves. + *

+ *

+ * Clients may instantiate this class (it was not designed to be subclassed). + *

+ *

+ * Unlike the FontRegistry, it is an error to replace images. As a result + * there are no events that fire when values are changed in the registry + *

+ */ +public class ImageRegistry { + /** + * display used when getting images + */ + private Display display; + + private ResourceManager manager; + + private Map!(String,Entry) table; + + private Runnable disposeRunnable; + private void init_disposeRunnable(){ + disposeRunnable = new class Runnable { + public void run() { + dispose(); + } + }; + } + + /** + * Contains the data for an entry in the registry. + */ + private static class Entry { + /** the image */ + protected Image image; + + /** the descriptor */ + protected ImageDescriptor descriptor; + } + + private static class OriginalImageDescriptor : ImageDescriptor { + private Image original; + private int refCount = 0; + private Device originalDisplay; + + /** + * @param original the original image + * @param originalDisplay the device the image is part of + */ + public this(Image original, Device originalDisplay) { + this.original = original; + this.originalDisplay = originalDisplay; + } + + public Object createResource(Device device) { + if (device is originalDisplay) { + refCount++; + return original; + } + return super.createResource(device); + } + + public void destroyResource(Object toDispose) { + if (original is toDispose) { + refCount--; + if (refCount is 0) { + original.dispose(); + original = null; + } + } else { + super.destroyResource(toDispose); + } + } + + /* (non-Javadoc) + * @see dwtx.jface.resource.ImageDescriptor#getImageData() + */ + public ImageData getImageData() { + return original.getImageData(); + } + } + + /** + * Creates an empty image registry. + *

+ * There must be an DWT Display created in the current + * thread before calling this method. + *

+ */ + public this() { + this(Display.getCurrent()); + } + + /** + * Creates an empty image registry using the given resource manager to allocate images. + * + * @param manager the resource manager used to allocate images + * + * @since 3.1 + */ + public this(ResourceManager manager) { + init_disposeRunnable(); + Assert.isNotNull(manager); + Device dev = manager.getDevice(); + if ( auto disp = cast(Display)dev ) { + this.display = disp; + } + this.manager = manager; + manager.disposeExec(disposeRunnable); + } + + /** + * Creates an empty image registry. + * + * @param display this Display must not be + * null and must not be disposed in order + * to use this registry + */ + public this(Display display) { + this(JFaceResources.getResources(display)); + } + + /** + * Returns the image associated with the given key in this registry, + * or null if none. + * + * @param key the key + * @return the image, or null if none + */ + public Image get(String key) { + + // can be null + if (key is null) { + return null; + } + + if (display !is null) { + /** + * NOTE, for backwards compatibility the following images are supported + * here, they should never be disposed, hence we explicitly return them + * rather then registering images that DWT will dispose. + * + * Applications should go direclty to DWT for these icons. + * + * @see Display.getSystemIcon(int ID) + */ + int swtKey = -1; + if (key.equals(Dialog.DLG_IMG_INFO)) { + swtKey = DWT.ICON_INFORMATION; + } + if (key.equals(Dialog.DLG_IMG_QUESTION)) { + swtKey = DWT.ICON_QUESTION; + } + if (key.equals(Dialog.DLG_IMG_WARNING)) { + swtKey = DWT.ICON_WARNING; + } + if (key.equals(Dialog.DLG_IMG_ERROR)) { + swtKey = DWT.ICON_ERROR; + } + // if we actually just want to return an DWT image do so without + // looking in the registry + if (swtKey !is -1) { + Image[1] image; + int id = swtKey; + display.syncExec(new class Runnable { + public void run() { + image[0] = display.getSystemImage(id); + } + }); + return image[0]; + } + } + + Entry entry = getEntry(key); + if (entry is null) { + return null; + } + + if (entry.image is null) { + entry.image = manager.createImageWithDefault(entry.descriptor); + } + + return entry.image; + } + + /** + * Returns the descriptor associated with the given key in this registry, + * or null if none. + * + * @param key the key + * @return the descriptor, or null if none + * @since 2.1 + */ + public ImageDescriptor getDescriptor(String key) { + Entry entry = getEntry(key); + if (entry is null) { + return null; + } + + return entry.descriptor; + } + + /** + * Adds (or replaces) an image descriptor to this registry. The first time + * this new entry is retrieved, the image descriptor's image will be computed + * (via ImageDescriptor.createImage) and remembered. + * This method replaces an existing image descriptor associated with the + * given key, but fails if there is a real image associated with it. + * + * @param key the key + * @param descriptor the ImageDescriptor + * @exception IllegalArgumentException if the key already exists + */ + public void put(String key, ImageDescriptor descriptor) { + Entry entry = getEntry(key); + if (entry is null) { + entry = new Entry(); + getTable().add(key, entry); + } + + if (entry.image !is null) { + throw new IllegalArgumentException( + "ImageRegistry key already in use: " ~ key); //$NON-NLS-1$ + } + + entry.descriptor = descriptor; + } + + /** + * Adds an image to this registry. This method fails if there + * is already an image or descriptor for the given key. + *

+ * Note that an image registry owns all of the image objects registered + * with it, and automatically disposes of them when the DWT Display is disposed. + * Because of this, clients must not register an image object + * that is managed by another object. + *

+ * + * @param key the key + * @param image the image, should not be null + * @exception IllegalArgumentException if the key already exists + */ + public void put(String key, Image image) { + Entry entry = getEntry(key); + + if (entry is null) { + entry = new Entry(); + putEntry(key, entry); + } + + if (entry.image !is null || entry.descriptor !is null) { + throw new IllegalArgumentException( + "ImageRegistry key already in use: " ~ key); //$NON-NLS-1$ + } + + // Should be checking for a null image here. + // Current behavior is that a null image won't be caught until dispose. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130315 + entry.image = image; + entry.descriptor = new OriginalImageDescriptor(image, manager.getDevice()); + + try { + manager.create(entry.descriptor); + } catch (DeviceResourceException e) { + } + } + + /** + * Removes an image from this registry. + * If an DWT image was allocated, it is disposed. + * This method has no effect if there is no image or descriptor for the given key. + * @param key the key + */ + public void remove(String key) { + ImageDescriptor descriptor = getDescriptor(key); + if (descriptor !is null) { + manager.destroy(descriptor); + getTable().removeKey(key); + } + } + + private Entry getEntry(String key) { + return cast(Entry) getTable().get(key); + } + + private void putEntry(String key, Entry entry) { + getTable().add(key, entry); + } + + private Map!(String,Entry) getTable() { + if (table is null) { + table = new HashMap!(String,Entry)/+(10)+/; + } + return table; + } + + /** + * Disposes this image registry, disposing any images + * that were allocated for it, and clearing its entries. + * + * @since 3.1 + */ + public void dispose() { + manager.cancelDisposeExec(disposeRunnable); + + if (table !is null) { + foreach( key,entry; table ){ + if (entry.image !is null) { + manager.destroyImage(entry.descriptor); + } + } + table = null; + } + display = null; + } +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/resource/JFaceResources.d --- a/dwtx/jface/resource/JFaceResources.d Fri Mar 28 23:32:40 2008 +0100 +++ b/dwtx/jface/resource/JFaceResources.d Sat Mar 29 01:25:27 2008 +0100 @@ -13,12 +13,21 @@ module dwtx.jface.resource.JFaceResources; import dwtx.jface.resource.FontRegistry; +import dwtx.jface.resource.ImageRegistry; +import dwtx.jface.resource.ResourceManager; + +import dwt.graphics.Font; +import dwt.widgets.Display; import dwt.dwthelper.utils; public class JFaceResources { public static const String DEFAULT_FONT = "dwtx.jface.defaultfont"; //$NON-NLS-1$ + public static final String DIALOG_FONT = "dwtx.jface.dialogfont"; //$NON-NLS-1$ public static FontRegistry getFontRegistry() ; public static String getString(String key) ; + public static Font getDialogFont() ; + public static ImageRegistry getImageRegistry() ; + public static ResourceManager getResources(Display toQuery) ; } /++ import java.text.MessageFormat; diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/resource/RGBColorDescriptor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/resource/RGBColorDescriptor.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.resource.RGBColorDescriptor; + +import dwtx.jface.resource.ColorDescriptor; + +import dwt.graphics.Color; +import dwt.graphics.Device; +import dwt.graphics.RGB; + +import dwt.dwthelper.utils; + +/** + * Describes a color by its RGB values. + * + * @since 3.1 + */ +class RGBColorDescriptor : ColorDescriptor { + + private RGB color; + + /** + * Color being copied, or null if none + */ + private Color originalColor = null; + + /** + * Creates a new RGBColorDescriptor given some RGB values + * + * @param color RGB values (not null) + */ + public this(RGB color) { + this.color = color; + } + + /** + * Creates a new RGBColorDescriptor that describes an existing color. + * + * @since 3.1 + * + * @param originalColor a color to describe + */ + public this(Color originalColor) { + this(originalColor.getRGB()); + this.originalColor = originalColor; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public override int opEquals(Object obj) { + if ( auto other = cast(RGBColorDescriptor)obj ) { + return other.color.opEquals(color) && other.originalColor is originalColor; + } + + return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public override hash_t toHash() { + return color.toHash(); + } + + /* (non-Javadoc) + * @see dwtx.jface.resources.ColorDescriptor#createColor() + */ + public Color createColor(Device device) { + // If this descriptor is wrapping an existing color, then we can return the original color + // if this is the same device. + if (originalColor !is null) { + // If we're allocating on the same device as the original color, return the original. + if (originalColor.getDevice() is device) { + return originalColor; + } + } + + return new Color(device, color); + } + + /* (non-Javadoc) + * @see dwtx.jface.resource.ColorDescriptor#destroyColor(dwt.graphics.Color) + */ + public void destroyColor(Color toDestroy) { + if (toDestroy is originalColor) { + return; + } + + toDestroy.dispose(); + } +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/resource/ResourceManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/resource/ResourceManager.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,406 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.resource.ResourceManager; + +import dwtx.jface.resource.DeviceResourceDescriptor; +import dwtx.jface.resource.ImageDescriptor; +import dwtx.jface.resource.ColorDescriptor; +import dwtx.jface.resource.FontDescriptor; +import dwtx.jface.resource.DeviceResourceException; +import dwtx.jface.resource.RGBColorDescriptor; + +// import java.util.ArrayList; +import tango.util.collection.model.Seq; +import tango.util.collection.ArraySeq; + +import dwt.DWTException; +import dwt.graphics.Color; +import dwt.graphics.Device; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.RGB; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.Status; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * This class manages DWT resources. It manages reference-counted instances of resources + * such as Fonts, Images, and Colors, and allows them to be accessed using descriptors. + * Everything allocated through the registry should also be disposed through the registry. + * Since the resources are shared and reference counted, they should never be disposed + * directly. + *

+ * ResourceManager handles correct allocation and disposal of resources. It differs from + * the various JFace *Registry classes, which also map symbolic IDs onto resources. In + * general, you should use a *Registry class to map IDs onto descriptors, and use a + * ResourceManager to convert the descriptors into real Images/Fonts/etc. + *

+ * + * @since 3.1 + */ +public abstract class ResourceManager { + + /** + * List of Runnables scheduled to run when the ResourceManager is disposed. + * null if empty. + */ + private Seq!(Runnable) disposeExecs = null; + + /** + * Returns the Device for which this ResourceManager will create resources + * + * @since 3.1 + * + * @return the Device associated with this ResourceManager + */ + public abstract Device getDevice(); + + /** + * Returns the resource described by the given descriptor. If the resource already + * exists, the reference count is incremented and the exiting resource is returned. + * Otherwise, a new resource is allocated. Every call to this method should have + * a corresponding call to {@link #destroy(DeviceResourceDescriptor)}. + * + *

If the resource is intended to live for entire lifetime of the resource manager, + * a subsequent call to {@link #destroy(DeviceResourceDescriptor)} may be omitted and the + * resource will be cleaned up when the resource manager is disposed. This pattern + * is useful for short-lived {@link LocalResourceManager}s, but should never be used + * with the global resource manager since doing so effectively leaks the resource.

+ * + *

The resources returned from this method are reference counted and may be shared + * internally with other resource managers. They should never be disposed outside of the + * ResourceManager framework, or it will cause exceptions in other code that shares + * them. For example, never call {@link dwt.graphics.Resource#dispose()} + * on anything returned from this method.

+ * + *

Callers may safely downcast the result to the resource type associated with + * the descriptor. For example, when given an ImageDescriptor, the return + * value of this method will always be an Image.

+ * + * @since 3.1 + * + * @param descriptor descriptor for the resource to allocate + * @return the newly allocated resource (not null) + * @throws DeviceResourceException if unable to allocate the resource + */ + public abstract Object create(DeviceResourceDescriptor descriptor); + + /** + * Deallocates a resource previously allocated by {@link #create(DeviceResourceDescriptor)}. + * Descriptors are compared by equality, not identity. If the same resource was + * created multiple times, this may decrement a reference count rather than + * disposing the actual resource. + * + * @since 3.1 + * + * @param descriptor identifier for the resource + */ + public abstract void destroy(DeviceResourceDescriptor descriptor); + + /** + *

Returns a previously-allocated resource or allocates a new one if none + * exists yet. The resource will remain allocated for at least the lifetime + * of this resource manager. If necessary, the resource will be deallocated + * automatically when the resource manager is disposed.

+ * + *

The resources returned from this method are reference counted and may be shared + * internally with other resource managers. They should never be disposed outside of the + * ResourceManager framework, or it will cause exceptions in other code that shares + * them. For example, never call {@link dwt.graphics.Resource#dispose()} + * on anything returned from this method.

+ * + *

+ * Callers may safely downcast the result to the resource type associated with + * the descriptor. For example, when given an ImageDescriptor, the return + * value of this method may be downcast to Image. + *

+ * + *

+ * This method should only be used for resources that should remain + * allocated for the lifetime of the resource manager. To allocate shorter-lived + * resources, manage them with create, and destroy + * rather than this method. + *

+ * + *

+ * This method should never be called on the global resource manager, + * since all resources will remain allocated for the lifetime of the app and + * will be effectively leaked. + *

+ * + * @param descriptor identifier for the requested resource + * @return the requested resource. Never null. + * @throws DeviceResourceException if the resource does not exist yet and cannot + * be created for any reason. + * + * @since 3.3 + */ + public final Object get(DeviceResourceDescriptor descriptor) { + Object result = find(descriptor); + + if (result is null) { + result = create(descriptor); + } + + return result; + } + + /** + *

Creates an image, given an image descriptor. Images allocated in this manner must + * be disposed by {@link #destroyImage(ImageDescriptor)}, and never by calling + * {@link Image#dispose()}.

+ * + *

+ * If the image is intended to remain allocated for the lifetime of the ResourceManager, + * the call to destroyImage may be omitted and the image will be cleaned up automatically + * when the ResourceManager is disposed. This should only be done with short-lived ResourceManagers, + * as doing so with the global manager effectively leaks the resource. + *

+ * + * @since 3.1 + * + * @param descriptor descriptor for the image to create + * @return the Image described by this descriptor (possibly shared by other equivalent + * ImageDescriptors) + * @throws DeviceResourceException if unable to allocate the Image + */ + public final Image createImage(ImageDescriptor descriptor) { + // Assertion added to help diagnose client bugs. See bug #83711 and bug #90454. + Assert.isNotNull(descriptor); + + return cast(Image)create(descriptor); + } + + /** + * Creates an image, given an image descriptor. Images allocated in this manner must + * be disposed by {@link #destroyImage(ImageDescriptor)}, and never by calling + * {@link Image#dispose()}. + * + * @since 3.1 + * + * @param descriptor descriptor for the image to create + * @return the Image described by this descriptor (possibly shared by other equivalent + * ImageDescriptors) + */ + public final Image createImageWithDefault(ImageDescriptor descriptor) { + if (descriptor is null) { + return getDefaultImage(); + } + + try { + return cast(Image) create(descriptor); + } catch (DeviceResourceException e) { + Policy.getLog().log( + new Status(IStatus.WARNING, "dwtx.jface", 0, //$NON-NLS-1$ + "The image could not be loaded: " ~ descriptor.toString, //$NON-NLS-1$ + e)); + return getDefaultImage(); + } catch (DWTException e) { + Policy.getLog().log( + new Status(IStatus.WARNING, "dwtx.jface", 0, //$NON-NLS-1$ + "The image could not be loaded: " ~ descriptor.toString, //$NON-NLS-1$ + e)); + return getDefaultImage(); + } + } + + /** + * Returns the default image that will be returned in the event that the intended + * image is missing. + * + * @since 3.1 + * + * @return a default image that will be returned in the event that the intended + * image is missing. + */ + protected abstract Image getDefaultImage(); + + /** + * Undoes everything that was done by {@link #createImage(ImageDescriptor)}. + * + * @since 3.1 + * + * @param descriptor identifier for the image to dispose + */ + public final void destroyImage(ImageDescriptor descriptor) { + destroy(descriptor); + } + + /** + * Allocates a color, given a color descriptor. Any color allocated in this + * manner must be disposed by calling {@link #destroyColor(ColorDescriptor)}, + * or by an eventual call to {@link #dispose()}. {@link Color#dispose()} must + * never been called directly on the returned color. + * + * @since 3.1 + * + * @param descriptor descriptor for the color to create + * @return the Color described by the given ColorDescriptor (not null) + * @throws DeviceResourceException if unable to create the color + */ + public final Color createColor(ColorDescriptor descriptor) { + return cast(Color)create(descriptor); + } + + /** + * Allocates a color, given its RGB value. Any color allocated in this + * manner must be disposed by calling {@link #destroyColor(RGB)}, + * or by an eventual call to {@link #dispose()}. {@link Color#dispose()} must + * never been called directly on the returned color. + * + * @since 3.1 + * + * @param descriptor descriptor for the color to create + * @return the Color described by the given ColorDescriptor (not null) + * @throws DeviceResourceException if unable to create the color + */ + public final Color createColor(RGB descriptor) { + return createColor(new RGBColorDescriptor(descriptor)); + } + + /** + * Undoes everything that was done by a call to {@link #createColor(RGB)}. + * + * @since 3.1 + * + * @param descriptor RGB value of the color to dispose + */ + public final void destroyColor(RGB descriptor) { + destroyColor(new RGBColorDescriptor(descriptor)); + } + + /** + * Undoes everything that was done by a call to {@link #createColor(ColorDescriptor)}. + * + * + * @since 3.1 + * + * @param descriptor identifier for the color to dispose + */ + public final void destroyColor(ColorDescriptor descriptor) { + destroy(descriptor); + } + + /** + * Returns the Font described by the given FontDescriptor. Any Font + * allocated in this manner must be deallocated by calling disposeFont(...), + * or by an eventual call to {@link #dispose()}. The method {@link Font#dispose()} + * must never be called directly on the returned font. + * + * @since 3.1 + * + * @param descriptor description of the font to create + * @return the Font described by the given descriptor + * @throws DeviceResourceException if unable to create the font + */ + public final Font createFont(FontDescriptor descriptor) { + return cast(Font)create(descriptor); + } + + /** + * Undoes everything that was done by a previous call to {@link #createFont(FontDescriptor)}. + * + * @since 3.1 + * + * @param descriptor description of the font to destroy + */ + public final void destroyFont(FontDescriptor descriptor) { + destroy(descriptor); + } + + /** + * Disposes any remaining resources allocated by this manager. + */ + public void dispose() { + if (disposeExecs is null) { + return; + } + + // If one of the runnables throws an exception, we need to propagate it. + // However, this should not prevent the remaining runnables from being + // notified. If any runnables throw an exception, we remember one of them + // here and throw it at the end of the method. + RuntimeException foundException = null; + + Runnable[] execs = disposeExecs.toArray(); + for (int i = 0; i < execs.length; i++) { + Runnable exec = execs[i]; + + try { + exec.run(); + } catch (RuntimeException e) { + // Ensure that we propagate an exception, but don't stop notifying + // the remaining runnables. + foundException = e; + } + } + + if (foundException !is null) { + // If any runnables threw an exception, propagate one of them. + throw foundException; + } + } + + /** + * Returns a previously allocated resource associated with the given descriptor, or + * null if none exists yet. + * + * @since 3.1 + * + * @param descriptor descriptor to find + * @return a previously allocated resource for the given descriptor or null if none. + */ + public abstract Object find(DeviceResourceDescriptor descriptor); + + /** + * Causes the run() method of the runnable to + * be invoked just before the receiver is disposed. The runnable + * can be subsequently canceled by a call to cancelDisposeExec. + * + * @param r runnable to execute. + */ + public void disposeExec(Runnable r) { + Assert.isNotNull(cast(Object)r); + + if (disposeExecs is null) { + disposeExecs = new ArraySeq!(Runnable); + } + + disposeExecs.append(r); + } + + /** + * Cancels a runnable that was previously scheduled with disposeExec. + * Has no effect if the given runnable was not previously registered with + * disposeExec. + * + * @param r runnable to cancel + */ + public void cancelDisposeExec(Runnable r) { + Assert.isNotNull(cast(Object)r); + + if (disposeExecs is null) { + return; + } + + disposeExecs.remove(r); + + if (disposeExecs.drained()) { + disposeExecs = null; + } + } +} diff -r 8a302fdb4140 -r a3ff22a98bef dwtx/jface/wizard/WizardDialog.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/wizard/WizardDialog.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,1520 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Chris Gross (schtoo@schtoo.com) - patch for bug 16179 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.wizard.WizardDialog; + +class WizardDialog { +} + + +/++ +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import dwt.DWT; +import dwt.custom.BusyIndicator; +import dwt.events.HelpEvent; +import dwt.events.HelpListener; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.graphics.Cursor; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.layout.GridData; +import dwt.layout.GridLayout; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Label; +import dwt.widgets.Layout; +import dwt.widgets.Shell; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IProgressMonitor; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.ListenerList; +import dwtx.jface.dialogs.ControlEnableState; +import dwtx.jface.dialogs.IDialogConstants; +import dwtx.jface.dialogs.IMessageProvider; +import dwtx.jface.dialogs.IPageChangeProvider; +import dwtx.jface.dialogs.IPageChangedListener; +import dwtx.jface.dialogs.IPageChangingListener; +import dwtx.jface.dialogs.MessageDialog; +import dwtx.jface.dialogs.PageChangedEvent; +import dwtx.jface.dialogs.PageChangingEvent; +import dwtx.jface.dialogs.TitleAreaDialog; +import dwtx.jface.operation.IRunnableWithProgress; +import dwtx.jface.operation.ModalContext; +import dwtx.jface.resource.JFaceResources; +import dwtx.jface.util.SafeRunnable; + +/** + * A dialog to show a wizard to the end user. + *

+ * In typical usage, the client instantiates this class with a particular + * wizard. The dialog serves as the wizard container and orchestrates the + * presentation of its pages. + *

+ * The standard layout is roughly as follows: it has an area at the top + * containing both the wizard's title, description, and image; the actual wizard + * page appears in the middle; below that is a progress indicator (which is made + * visible if needed); and at the bottom of the page is message line and a + * button bar containing Help, Next, Back, Finish, and Cancel buttons (or some + * subset). + *

+ *

+ * Clients may subclass WizardDialog, although this is rarely + * required. + *

+ */ +public class WizardDialog extends TitleAreaDialog implements IWizardContainer2, + IPageChangeProvider { + /** + * Image registry key for error message image (value + * "dialog_title_error_image"). + */ + public static final String WIZ_IMG_ERROR = "dialog_title_error_image"; //$NON-NLS-1$ + + // The wizard the dialog is currently showing. + private IWizard wizard; + + // Wizards to dispose + private ArrayList createdWizards = new ArrayList(); + + // Current nested wizards + private ArrayList nestedWizards = new ArrayList(); + + // The currently displayed page. + private IWizardPage currentPage = null; + + // The number of long running operation executed from the dialog. + private long activeRunningOperations = 0; + + // The current page message and description + private String pageMessage; + + private int pageMessageType = IMessageProvider.NONE; + + private String pageDescription; + + // The progress monitor + private ProgressMonitorPart progressMonitorPart; + + private Cursor waitCursor; + + private Cursor arrowCursor; + + private MessageDialog windowClosingDialog; + + // Navigation buttons + private Button backButton; + + private Button nextButton; + + private Button finishButton; + + private Button cancelButton; + + private Button helpButton; + + private SelectionAdapter cancelListener; + + private bool isMovingToPreviousPage = false; + + private Composite pageContainer; + + private PageContainerFillLayout pageContainerLayout = new PageContainerFillLayout( + 5, 5, 300, 225); + + private int pageWidth = DWT.DEFAULT; + + private int pageHeight = DWT.DEFAULT; + + private static final String FOCUS_CONTROL = "focusControl"; //$NON-NLS-1$ + + private bool lockedUI = false; + + private ListenerList pageChangedListeners = new ListenerList(); + + private ListenerList pageChangingListeners = new ListenerList(); + + /** + * A layout for a container which includes several pages, like a notebook, + * wizard, or preference dialog. The size computed by this layout is the + * maximum width and height of all pages currently inserted into the + * container. + */ + protected class PageContainerFillLayout extends Layout { + /** + * The margin width; 5 pixels by default. + */ + public int marginWidth = 5; + + /** + * The margin height; 5 pixels by default. + */ + public int marginHeight = 5; + + /** + * The minimum width; 0 pixels by default. + */ + public int minimumWidth = 0; + + /** + * The minimum height; 0 pixels by default. + */ + public int minimumHeight = 0; + + /** + * Creates new layout object. + * + * @param mw + * the margin width + * @param mh + * the margin height + * @param minW + * the minimum width + * @param minH + * the minimum height + */ + public PageContainerFillLayout(int mw, int mh, int minW, int minH) { + marginWidth = mw; + marginHeight = mh; + minimumWidth = minW; + minimumHeight = minH; + } + + /* + * (non-Javadoc) Method declared on Layout. + */ + public Point computeSize(Composite composite, int wHint, int hHint, + bool force) { + if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) { + return new Point(wHint, hHint); + } + Point result = null; + Control[] children = composite.getChildren(); + if (children.length > 0) { + result = new Point(0, 0); + for (int i = 0; i < children.length; i++) { + Point cp = children[i].computeSize(wHint, hHint, force); + result.x = Math.max(result.x, cp.x); + result.y = Math.max(result.y, cp.y); + } + result.x = result.x + 2 * marginWidth; + result.y = result.y + 2 * marginHeight; + } else { + Rectangle rect = composite.getClientArea(); + result = new Point(rect.width, rect.height); + } + result.x = Math.max(result.x, minimumWidth); + result.y = Math.max(result.y, minimumHeight); + if (wHint !is DWT.DEFAULT) { + result.x = wHint; + } + if (hHint !is DWT.DEFAULT) { + result.y = hHint; + } + return result; + } + + /** + * Returns the client area for the given composite according to this + * layout. + * + * @param c + * the composite + * @return the client area rectangle + */ + public Rectangle getClientArea(Composite c) { + Rectangle rect = c.getClientArea(); + rect.x = rect.x + marginWidth; + rect.y = rect.y + marginHeight; + rect.width = rect.width - 2 * marginWidth; + rect.height = rect.height - 2 * marginHeight; + return rect; + } + + /* + * (non-Javadoc) Method declared on Layout. + */ + public void layout(Composite composite, bool force) { + Rectangle rect = getClientArea(composite); + Control[] children = composite.getChildren(); + for (int i = 0; i < children.length; i++) { + children[i].setBounds(rect); + } + } + + /** + * Lays outs the page according to this layout. + * + * @param w + * the control + */ + public void layoutPage(Control w) { + w.setBounds(getClientArea(w.getParent())); + } + + /** + * Sets the location of the page so that its origin is in the upper left + * corner. + * + * @param w + * the control + */ + public void setPageLocation(Control w) { + w.setLocation(marginWidth, marginHeight); + } + } + + /** + * Creates a new wizard dialog for the given wizard. + * + * @param parentShell + * the parent shell + * @param newWizard + * the wizard this dialog is working on + */ + public WizardDialog(Shell parentShell, IWizard newWizard) { + super(parentShell); + setShellStyle(DWT.CLOSE | DWT.MAX | DWT.TITLE | DWT.BORDER + | DWT.APPLICATION_MODAL | DWT.RESIZE | getDefaultOrientation()); + setWizard(newWizard); + // since VAJava can't initialize an instance var with an anonymous + // class outside a constructor we do it here: + cancelListener = new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + cancelPressed(); + } + }; + } + + /** + * About to start a long running operation triggered through the wizard. + * Shows the progress monitor and disables the wizard's buttons and + * controls. + * + * @param enableCancelButton + * true if the Cancel button should be enabled, + * and false if it should be disabled + * @return the saved UI state + */ + private Object aboutToStart(bool enableCancelButton) { + Map savedState = null; + if (getShell() !is null) { + // Save focus control + Control focusControl = getShell().getDisplay().getFocusControl(); + if (focusControl !is null && focusControl.getShell() !is getShell()) { + focusControl = null; + } + bool needsProgressMonitor = wizard.needsProgressMonitor(); + cancelButton.removeSelectionListener(cancelListener); + // Set the busy cursor to all shells. + Display d = getShell().getDisplay(); + waitCursor = new Cursor(d, DWT.CURSOR_WAIT); + setDisplayCursor(waitCursor); + // Set the arrow cursor to the cancel component. + arrowCursor = new Cursor(d, DWT.CURSOR_ARROW); + cancelButton.setCursor(arrowCursor); + // Deactivate shell + savedState = saveUIState(needsProgressMonitor && enableCancelButton); + if (focusControl !is null) { + savedState.put(FOCUS_CONTROL, focusControl); + } + // Attach the progress monitor part to the cancel button + if (needsProgressMonitor) { + progressMonitorPart.attachToCancelComponent(cancelButton); + progressMonitorPart.setVisible(true); + } + } + return savedState; + } + + /** + * The Back button has been pressed. + */ + protected void backPressed() { + IWizardPage page = currentPage.getPreviousPage(); + if (page is null) { + // should never happen since we have already visited the page + return; + } + + // set flag to indicate that we are moving back + isMovingToPreviousPage = true; + // show the page + showPage(page); + } + + /* + * (non-Javadoc) Method declared on Dialog. + */ + protected void buttonPressed(int buttonId) { + switch (buttonId) { + case IDialogConstants.HELP_ID: { + helpPressed(); + break; + } + case IDialogConstants.BACK_ID: { + backPressed(); + break; + } + case IDialogConstants.NEXT_ID: { + nextPressed(); + break; + } + case IDialogConstants.FINISH_ID: { + finishPressed(); + break; + } + // The Cancel button has a listener which calls cancelPressed + // directly + } + } + + /** + * Calculates the difference in size between the given page and the page + * container. A larger page results in a positive delta. + * + * @param page + * the page + * @return the size difference encoded as a + * new Point(deltaWidth,deltaHeight) + */ + private Point calculatePageSizeDelta(IWizardPage page) { + Control pageControl = page.getControl(); + if (pageControl is null) { + // control not created yet + return new Point(0, 0); + } + Point contentSize = pageControl.computeSize(DWT.DEFAULT, DWT.DEFAULT, + true); + Rectangle rect = pageContainerLayout.getClientArea(pageContainer); + Point containerSize = new Point(rect.width, rect.height); + return new Point(Math.max(0, contentSize.x - containerSize.x), Math + .max(0, contentSize.y - containerSize.y)); + } + + /* + * (non-Javadoc) Method declared on Dialog. + */ + protected void cancelPressed() { + if (activeRunningOperations <= 0) { + // Close the dialog. The check whether the dialog can be + // closed or not is done in okToClose. + // This ensures that the check is also evaluated when the user + // presses the window's close button. + setReturnCode(CANCEL); + close(); + } else { + cancelButton.setEnabled(false); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#close() + */ + public bool close() { + if (okToClose()) { + return hardClose(); + } + return false; + } + + /* + * (non-Javadoc) Method declared on Window. + */ + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + // Register help listener on the shell + newShell.addHelpListener(new HelpListener() { + public void helpRequested(HelpEvent event) { + // call perform help on the current page + if (currentPage !is null) { + currentPage.performHelp(); + } + } + }); + } + + /** + * Creates the buttons for this dialog's button bar. + *

+ * The WizardDialog implementation of this framework method + * prevents the parent composite's columns from being made equal width in + * order to remove the margin between the Back and Next buttons. + *

+ * + * @param parent + * the parent composite to contain the buttons + */ + protected void createButtonsForButtonBar(Composite parent) { + ((GridLayout) parent.getLayout()).makeColumnsEqualWidth = false; + if (wizard.isHelpAvailable()) { + helpButton = createButton(parent, IDialogConstants.HELP_ID, + IDialogConstants.HELP_LABEL, false); + } + if (wizard.needsPreviousAndNextButtons()) { + createPreviousAndNextButtons(parent); + } + finishButton = createButton(parent, IDialogConstants.FINISH_ID, + IDialogConstants.FINISH_LABEL, true); + cancelButton = createCancelButton(parent); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.Dialog#setButtonLayoutData(dwt.widgets.Button) + */ + protected void setButtonLayoutData(Button button) { + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); + + // On large fonts this can make this dialog huge + widthHint = Math.min(widthHint, + button.getDisplay().getBounds().width / 5); + Point minSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, true); + data.widthHint = Math.max(widthHint, minSize.x); + + button.setLayoutData(data); + } + + /** + * Creates the Cancel button for this wizard dialog. Creates a standard (DWT.PUSH) + * button and registers for its selection events. Note that the number of + * columns in the button bar composite is incremented. The Cancel button is + * created specially to give it a removeable listener. + * + * @param parent + * the parent button bar + * @return the new Cancel button + */ + private Button createCancelButton(Composite parent) { + // increment the number of columns in the button bar + ((GridLayout) parent.getLayout()).numColumns++; + Button button = new Button(parent, DWT.PUSH); + button.setText(IDialogConstants.CANCEL_LABEL); + setButtonLayoutData(button); + button.setFont(parent.getFont()); + button.setData(new Integer(IDialogConstants.CANCEL_ID)); + button.addSelectionListener(cancelListener); + return button; + } + + /** + * Return the cancel button if the id is a the cancel id. + * + * @param id + * the button id + * @return the button corresponding to the button id + */ + protected Button getButton(int id) { + if (id is IDialogConstants.CANCEL_ID) { + return cancelButton; + } + return super.getButton(id); + } + + /** + * The WizardDialog implementation of this + * Window method calls call IWizard.addPages + * to allow the current wizard to add extra pages, then + * super.createContents to create the controls. It then calls + * IWizard.createPageControls to allow the wizard to + * pre-create their page controls prior to opening, so that the wizard opens + * to the correct size. And finally it shows the first page. + */ + protected Control createContents(Composite parent) { + // Allow the wizard to add pages to itself + // Need to call this now so page count is correct + // for determining if next/previous buttons are needed + wizard.addPages(); + Control contents = super.createContents(parent); + // Allow the wizard pages to precreate their page controls + createPageControls(); + // Show the first page + showStartingPage(); + return contents; + } + + /* + * (non-Javadoc) Method declared on Dialog. + */ + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + // Build the Page container + pageContainer = createPageContainer(composite); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.widthHint = pageWidth; + gd.heightHint = pageHeight; + pageContainer.setLayoutData(gd); + pageContainer.setFont(parent.getFont()); + // Insert a progress monitor + GridLayout pmlayout = new GridLayout(); + pmlayout.numColumns = 1; + progressMonitorPart = createProgressMonitorPart(composite, pmlayout); + GridData gridData = new GridData(GridData.FILL_HORIZONTAL); + progressMonitorPart.setLayoutData(gridData); + progressMonitorPart.setVisible(false); + // Build the separator line + Label separator = new Label(composite, DWT.HORIZONTAL | DWT.SEPARATOR); + separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + applyDialogFont(progressMonitorPart); + return composite; + } + + /** + * Create the progress monitor part in the receiver. + * + * @param composite + * @param pmlayout + * @return ProgressMonitorPart + */ + protected ProgressMonitorPart createProgressMonitorPart( + Composite composite, GridLayout pmlayout) { + return new ProgressMonitorPart(composite, pmlayout, DWT.DEFAULT) { + String currentTask = null; + + /* + * (non-Javadoc) + * + * @see dwtx.jface.wizard.ProgressMonitorPart#setBlocked(dwtx.core.runtime.IStatus) + */ + public void setBlocked(IStatus reason) { + super.setBlocked(reason); + if (!lockedUI) { + getBlockedHandler().showBlocked(getShell(), this, reason, + currentTask); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.wizard.ProgressMonitorPart#clearBlocked() + */ + public void clearBlocked() { + super.clearBlocked(); + if (!lockedUI) { + getBlockedHandler().clearBlocked(); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.wizard.ProgressMonitorPart#beginTask(java.lang.String, + * int) + */ + public void beginTask(String name, int totalWork) { + super.beginTask(name, totalWork); + currentTask = name; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.wizard.ProgressMonitorPart#setTaskName(java.lang.String) + */ + public void setTaskName(String name) { + super.setTaskName(name); + currentTask = name; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.wizard.ProgressMonitorPart#subTask(java.lang.String) + */ + public void subTask(String name) { + super.subTask(name); + // If we haven't got anything yet use this value for more + // context + if (currentTask is null) { + currentTask = name; + } + } + }; + } + + /** + * Creates the container that holds all pages. + * + * @param parent + * @return Composite + */ + private Composite createPageContainer(Composite parent) { + Composite result = new Composite(parent, DWT.NULL); + result.setLayout(pageContainerLayout); + return result; + } + + /** + * Allow the wizard's pages to pre-create their page controls. This allows + * the wizard dialog to open to the correct size. + */ + private void createPageControls() { + // Allow the wizard pages to precreate their page controls + // This allows the wizard to open to the correct size + wizard.createPageControls(pageContainer); + // Ensure that all of the created pages are initially not visible + IWizardPage[] pages = wizard.getPages(); + for (int i = 0; i < pages.length; i++) { + IWizardPage page = pages[i]; + if (page.getControl() !is null) { + page.getControl().setVisible(false); + } + } + } + + /** + * Creates the Previous and Next buttons for this wizard dialog. Creates + * standard (DWT.PUSH) buttons and registers for their + * selection events. Note that the number of columns in the button bar + * composite is incremented. These buttons are created specially to prevent + * any space between them. + * + * @param parent + * the parent button bar + * @return a composite containing the new buttons + */ + private Composite createPreviousAndNextButtons(Composite parent) { + // increment the number of columns in the button bar + ((GridLayout) parent.getLayout()).numColumns++; + Composite composite = new Composite(parent, DWT.NONE); + // create a layout with spacing and margins appropriate for the font + // size. + GridLayout layout = new GridLayout(); + layout.numColumns = 0; // will be incremented by createButton + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + composite.setLayout(layout); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER + | GridData.VERTICAL_ALIGN_CENTER); + composite.setLayoutData(data); + composite.setFont(parent.getFont()); + backButton = createButton(composite, IDialogConstants.BACK_ID, + IDialogConstants.BACK_LABEL, false); + nextButton = createButton(composite, IDialogConstants.NEXT_ID, + IDialogConstants.NEXT_LABEL, false); + return composite; + } + + /** + * Creates and return a new wizard closing dialog without openiong it. + * + * @return MessageDalog + */ + private MessageDialog createWizardClosingDialog() { + MessageDialog result = new MessageDialog(getShell(), + JFaceResources.getString("WizardClosingDialog.title"), //$NON-NLS-1$ + null, + JFaceResources.getString("WizardClosingDialog.message"), //$NON-NLS-1$ + MessageDialog.QUESTION, + new String[] { IDialogConstants.OK_LABEL }, 0); + return result; + } + + /** + * The Finish button has been pressed. + */ + protected void finishPressed() { + // Wizards are added to the nested wizards list in setWizard. + // This means that the current wizard is always the last wizard in the + // list. + // Note that we first call the current wizard directly (to give it a + // chance to + // abort, do work, and save state) then call the remaining n-1 wizards + // in the + // list (to save state). + if (wizard.performFinish()) { + // Call perform finish on outer wizards in the nested chain + // (to allow them to save state for example) + for (int i = 0; i < nestedWizards.size() - 1; i++) { + ((IWizard) nestedWizards.get(i)).performFinish(); + } + // Hard close the dialog. + setReturnCode(OK); + hardClose(); + } + } + + /* + * (non-Javadoc) Method declared on IWizardContainer. + */ + public IWizardPage getCurrentPage() { + return currentPage; + } + + /** + * Returns the progress monitor for this wizard dialog (if it has one). + * + * @return the progress monitor, or null if this wizard + * dialog does not have one + */ + protected IProgressMonitor getProgressMonitor() { + return progressMonitorPart; + } + + /** + * Returns the wizard this dialog is currently displaying. + * + * @return the current wizard + */ + protected IWizard getWizard() { + return wizard; + } + + /** + * Closes this window. + * + * @return true if the window is (or was already) closed, and + * false if it is still open + */ + private bool hardClose() { + // inform wizards + for (int i = 0; i < createdWizards.size(); i++) { + IWizard createdWizard = (IWizard) createdWizards.get(i); + createdWizard.dispose(); + // Remove this dialog as a parent from the managed wizard. + // Note that we do this after calling dispose as the wizard or + // its pages may need access to the container during + // dispose code + createdWizard.setContainer(null); + } + return super.close(); + } + + /** + * The Help button has been pressed. + */ + protected void helpPressed() { + if (currentPage !is null) { + currentPage.performHelp(); + } + } + + /** + * The Next button has been pressed. + */ + protected void nextPressed() { + IWizardPage page = currentPage.getNextPage(); + if (page is null) { + // something must have happend getting the next page + return; + } + + // show the next page + showPage(page); + } + + /** + * Notifies page changing listeners and returns result of page changing + * processing to the sender. + * + * @param eventType + * @return true if page changing listener completes + * successfully, false otherwise + */ + private bool doPageChanging(IWizardPage targetPage) { + PageChangingEvent e = new PageChangingEvent(this, getCurrentPage(), + targetPage); + firePageChanging(e); + // Prevent navigation if necessary + return e.doit; + } + + /** + * Checks whether it is alright to close this wizard dialog and performed + * standard cancel processing. If there is a long running operation in + * progress, this method posts an alert message saying that the wizard + * cannot be closed. + * + * @return true if it is alright to close this dialog, and + * false if it is not + */ + private bool okToClose() { + if (activeRunningOperations > 0) { + synchronized (this) { + windowClosingDialog = createWizardClosingDialog(); + } + windowClosingDialog.open(); + synchronized (this) { + windowClosingDialog = null; + } + return false; + } + return wizard.performCancel(); + } + + /** + * Restores the enabled/disabled state of the given control. + * + * @param w + * the control + * @param h + * the map (key type: String, element type: + * bool) + * @param key + * the key + * @see #saveEnableStateAndSet + */ + private void restoreEnableState(Control w, Map h, String key) { + if (w !is null) { + bool b = (bool) h.get(key); + if (b !is null) { + w.setEnabled(b.booleanValue()); + } + } + } + + /** + * Restores the enabled/disabled state of the wizard dialog's buttons and + * the tree of controls for the currently showing page. + * + * @param state + * a map containing the saved state as returned by + * saveUIState + * @see #saveUIState + */ + private void restoreUIState(Map state) { + restoreEnableState(backButton, state, "back"); //$NON-NLS-1$ + restoreEnableState(nextButton, state, "next"); //$NON-NLS-1$ + restoreEnableState(finishButton, state, "finish"); //$NON-NLS-1$ + restoreEnableState(cancelButton, state, "cancel"); //$NON-NLS-1$ + restoreEnableState(helpButton, state, "help"); //$NON-NLS-1$ + Object pageValue = state.get("page"); //$NON-NLS-1$ + if (pageValue !is null) { + ((ControlEnableState) pageValue).restore(); + } + } + + /** + * This implementation of IRunnableContext#run(bool, bool, + * IRunnableWithProgress) blocks until the runnable has been run, regardless + * of the value of fork. It is recommended that + * fork is set to true in most cases. If fork + * is set to false, the runnable will run in the UI thread + * and it is the runnable's responsibility to call + * Display.readAndDispatch() to ensure UI responsiveness. + * + * UI state is saved prior to executing the long-running operation and is + * restored after the long-running operation completes executing. Any + * attempt to change the UI state of the wizard in the long-running + * operation will be nullified when original UI state is restored. + * + */ + public void run(bool fork, bool cancelable, + IRunnableWithProgress runnable) throws InvocationTargetException, + InterruptedException { + // The operation can only be canceled if it is executed in a separate + // thread. + // Otherwise the UI is blocked anyway. + Object state = null; + if (activeRunningOperations is 0) { + state = aboutToStart(fork && cancelable); + } + activeRunningOperations++; + try { + if (!fork) { + lockedUI = true; + } + ModalContext.run(runnable, fork, getProgressMonitor(), getShell() + .getDisplay()); + lockedUI = false; + } finally { + activeRunningOperations--; + // Stop if this is the last one + if (state !is null) { + stopped(state); + } + } + } + + /** + * Saves the enabled/disabled state of the given control in the given map, + * which must be modifiable. + * + * @param w + * the control, or null if none + * @param h + * the map (key type: String, element type: + * bool) + * @param key + * the key + * @param enabled + * true to enable the control, and + * false to disable it + * @see #restoreEnableState(Control, Map, String) + */ + private void saveEnableStateAndSet(Control w, Map h, String key, + bool enabled) { + if (w !is null) { + h.put(key, w.getEnabled() ? bool.TRUE : bool.FALSE); + w.setEnabled(enabled); + } + } + + /** + * Captures and returns the enabled/disabled state of the wizard dialog's + * buttons and the tree of controls for the currently showing page. All + * these controls are disabled in the process, with the possible exception + * of the Cancel button. + * + * @param keepCancelEnabled + * true if the Cancel button should remain + * enabled, and false if it should be disabled + * @return a map containing the saved state suitable for restoring later + * with restoreUIState + * @see #restoreUIState + */ + private Map saveUIState(bool keepCancelEnabled) { + Map savedState = new HashMap(10); + saveEnableStateAndSet(backButton, savedState, "back", false); //$NON-NLS-1$ + saveEnableStateAndSet(nextButton, savedState, "next", false); //$NON-NLS-1$ + saveEnableStateAndSet(finishButton, savedState, "finish", false); //$NON-NLS-1$ + saveEnableStateAndSet(cancelButton, savedState, + "cancel", keepCancelEnabled); //$NON-NLS-1$ + saveEnableStateAndSet(helpButton, savedState, "help", false); //$NON-NLS-1$ + if (currentPage !is null) { + savedState + .put( + "page", ControlEnableState.disable(currentPage.getControl())); //$NON-NLS-1$ + } + return savedState; + } + + /** + * Sets the given cursor for all shells currently active for this window's + * display. + * + * @param c + * the cursor + */ + private void setDisplayCursor(Cursor c) { + Shell[] shells = getShell().getDisplay().getShells(); + for (int i = 0; i < shells.length; i++) { + shells[i].setCursor(c); + } + } + + /** + * Sets the minimum page size used for the pages. + * + * @param minWidth + * the minimum page width + * @param minHeight + * the minimum page height + * @see #setMinimumPageSize(Point) + */ + public void setMinimumPageSize(int minWidth, int minHeight) { + Assert.isTrue(minWidth >= 0 && minHeight >= 0); + pageContainerLayout.minimumWidth = minWidth; + pageContainerLayout.minimumHeight = minHeight; + } + + /** + * Sets the minimum page size used for the pages. + * + * @param size + * the page size encoded as new Point(width,height) + * @see #setMinimumPageSize(int,int) + */ + public void setMinimumPageSize(Point size) { + setMinimumPageSize(size.x, size.y); + } + + /** + * Sets the size of all pages. The given size takes precedence over computed + * sizes. + * + * @param width + * the page width + * @param height + * the page height + * @see #setPageSize(Point) + */ + public void setPageSize(int width, int height) { + pageWidth = width; + pageHeight = height; + } + + /** + * Sets the size of all pages. The given size takes precedence over computed + * sizes. + * + * @param size + * the page size encoded as new Point(width,height) + * @see #setPageSize(int,int) + */ + public void setPageSize(Point size) { + setPageSize(size.x, size.y); + } + + /** + * Sets the wizard this dialog is currently displaying. + * + * @param newWizard + * the wizard + */ + protected void setWizard(IWizard newWizard) { + wizard = newWizard; + wizard.setContainer(this); + if (!createdWizards.contains(wizard)) { + createdWizards.add(wizard); + // New wizard so just add it to the end of our nested list + nestedWizards.add(wizard); + if (pageContainer !is null) { + // Dialog is already open + // Allow the wizard pages to precreate their page controls + // This allows the wizard to open to the correct size + createPageControls(); + // Ensure the dialog is large enough for the wizard + updateSizeForWizard(wizard); + pageContainer.layout(true); + } + } else { + // We have already seen this wizard, if it is the previous wizard + // on the nested list then we assume we have gone back and remove + // the last wizard from the list + int size = nestedWizards.size(); + if (size >= 2 && nestedWizards.get(size - 2) is wizard) { + nestedWizards.remove(size - 1); + } else { + // Assume we are going forward to revisit a wizard + nestedWizards.add(wizard); + } + } + } + + /* + * (non-Javadoc) Method declared on IWizardContainer. + */ + public void showPage(IWizardPage page) { + if (page is null || page is currentPage) { + return; + } + + if (!isMovingToPreviousPage) { + // remember my previous page. + page.setPreviousPage(currentPage); + } else { + isMovingToPreviousPage = false; + } + + // If page changing evaluation unsuccessful, do not change the page + if (!doPageChanging(page)) + return; + + // Update for the new page in a busy cursor if possible + if (getContents() is null) { + updateForPage(page); + } else { + final IWizardPage finalPage = page; + BusyIndicator.showWhile(getContents().getDisplay(), new Runnable() { + public void run() { + updateForPage(finalPage); + } + }); + } + } + + /** + * Update the receiver for the new page. + * + * @param page + */ + private void updateForPage(IWizardPage page) { + // ensure this page belongs to the current wizard + if (wizard !is page.getWizard()) { + setWizard(page.getWizard()); + } + // ensure that page control has been created + // (this allows lazy page control creation) + if (page.getControl() is null) { + page.createControl(pageContainer); + // the page is responsible for ensuring the created control is + // accessable + // via getControl. + Assert.isNotNull(page.getControl(), JFaceResources.format( + JFaceResources.getString("WizardDialog.missingSetControl"), //$NON-NLS-1$ + new Object[] { page.getName() })); + // ensure the dialog is large enough for this page + updateSize(page); + } + // make the new page visible + IWizardPage oldPage = currentPage; + currentPage = page; + + currentPage.setVisible(true); + if (oldPage !is null) { + oldPage.setVisible(false); + } + // update the dialog controls + update(); + } + + /** + * Shows the starting page of the wizard. + */ + private void showStartingPage() { + currentPage = wizard.getStartingPage(); + if (currentPage is null) { + // something must have happend getting the page + return; + } + // ensure the page control has been created + if (currentPage.getControl() is null) { + currentPage.createControl(pageContainer); + // the page is responsible for ensuring the created control is + // accessable + // via getControl. + Assert.isNotNull(currentPage.getControl()); + // we do not need to update the size since the call + // to initialize bounds has not been made yet. + } + // make the new page visible + currentPage.setVisible(true); + // update the dialog controls + update(); + } + + /** + * A long running operation triggered through the wizard was stopped either + * by user input or by normal end. Hides the progress monitor and restores + * the enable state wizard's buttons and controls. + * + * @param savedState + * the saved UI state as returned by aboutToStart + * @see #aboutToStart + */ + private void stopped(Object savedState) { + if (getShell() !is null) { + if (wizard.needsProgressMonitor()) { + progressMonitorPart.setVisible(false); + progressMonitorPart.removeFromCancelComponent(cancelButton); + } + Map state = (Map) savedState; + restoreUIState(state); + cancelButton.addSelectionListener(cancelListener); + setDisplayCursor(null); + cancelButton.setCursor(null); + waitCursor.dispose(); + waitCursor = null; + arrowCursor.dispose(); + arrowCursor = null; + Control focusControl = (Control) state.get(FOCUS_CONTROL); + if (focusControl !is null) { + focusControl.setFocus(); + } + } + } + + /** + * Updates this dialog's controls to reflect the current page. + */ + protected void update() { + // Update the window title + updateWindowTitle(); + // Update the title bar + updateTitleBar(); + // Update the buttons + updateButtons(); + + // Fires the page change event + firePageChanged(new PageChangedEvent(this, getCurrentPage())); + } + + /* + * (non-Javadoc) Method declared on IWizardContainer. + */ + public void updateButtons() { + bool canFlipToNextPage = false; + bool canFinish = wizard.canFinish(); + if (backButton !is null) { + backButton.setEnabled(currentPage.getPreviousPage() !is null); + } + if (nextButton !is null) { + canFlipToNextPage = currentPage.canFlipToNextPage(); + nextButton.setEnabled(canFlipToNextPage); + } + finishButton.setEnabled(canFinish); + // finish is default unless it is diabled and next is enabled + if (canFlipToNextPage && !canFinish) { + getShell().setDefaultButton(nextButton); + } else { + getShell().setDefaultButton(finishButton); + } + } + + /** + * Update the message line with the page's description. + *

+ * A discription is shown only if there is no message or error message. + *

+ */ + private void updateDescriptionMessage() { + pageDescription = currentPage.getDescription(); + setMessage(pageDescription); + } + + /* + * (non-Javadoc) Method declared on IWizardContainer. + */ + public void updateMessage() { + + if (currentPage is null) { + return; + } + + pageMessage = currentPage.getMessage(); + if (pageMessage !is null && currentPage instanceof IMessageProvider) { + pageMessageType = ((IMessageProvider) currentPage).getMessageType(); + } else { + pageMessageType = IMessageProvider.NONE; + } + if (pageMessage is null) { + setMessage(pageDescription); + } else { + setMessage(pageMessage, pageMessageType); + } + setErrorMessage(currentPage.getErrorMessage()); + } + + /** + * Changes the shell size to the given size, ensuring that it is no larger + * than the display bounds. + * + * @param width + * the shell width + * @param height + * the shell height + */ + private void setShellSize(int width, int height) { + Rectangle size = getShell().getBounds(); + size.height = height; + size.width = width; + getShell().setBounds(getConstrainedShellBounds(size)); + } + + /** + * Computes the correct dialog size for the current page and resizes its + * shell if nessessary. Also causes the container to refresh its layout. + * + * @param page + * the wizard page to use to resize the dialog + * @since 2.0 + */ + protected void updateSize(IWizardPage page) { + if (page is null || page.getControl() is null) { + return; + } + updateSizeForPage(page); + pageContainerLayout.layoutPage(page.getControl()); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.wizard.IWizardContainer2#updateSize() + */ + public void updateSize() { + updateSize(currentPage); + } + + /** + * Computes the correct dialog size for the given page and resizes its shell + * if nessessary. + * + * @param page + * the wizard page + */ + private void updateSizeForPage(IWizardPage page) { + // ensure the page container is large enough + Point delta = calculatePageSizeDelta(page); + if (delta.x > 0 || delta.y > 0) { + // increase the size of the shell + Shell shell = getShell(); + Point shellSize = shell.getSize(); + setShellSize(shellSize.x + delta.x, shellSize.y + delta.y); + constrainShellSize(); + } + } + + /** + * Computes the correct dialog size for the given wizard and resizes its + * shell if nessessary. + * + * @param sizingWizard + * the wizard + */ + private void updateSizeForWizard(IWizard sizingWizard) { + Point delta = new Point(0, 0); + IWizardPage[] pages = sizingWizard.getPages(); + for (int i = 0; i < pages.length; i++) { + // ensure the page container is large enough + Point pageDelta = calculatePageSizeDelta(pages[i]); + delta.x = Math.max(delta.x, pageDelta.x); + delta.y = Math.max(delta.y, pageDelta.y); + } + if (delta.x > 0 || delta.y > 0) { + // increase the size of the shell + Shell shell = getShell(); + Point shellSize = shell.getSize(); + setShellSize(shellSize.x + delta.x, shellSize.y + delta.y); + } + } + + /* + * (non-Javadoc) Method declared on IWizardContainer. + */ + public void updateTitleBar() { + String s = null; + if (currentPage !is null) { + s = currentPage.getTitle(); + } + if (s is null) { + s = ""; //$NON-NLS-1$ + } + setTitle(s); + if (currentPage !is null) { + setTitleImage(currentPage.getImage()); + updateDescriptionMessage(); + } + updateMessage(); + } + + /* + * (non-Javadoc) Method declared on IWizardContainer. + */ + public void updateWindowTitle() { + if (getShell() is null) { + // Not created yet + return; + } + String title = wizard.getWindowTitle(); + if (title is null) { + title = ""; //$NON-NLS-1$ + } + getShell().setText(title); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.IPageChangeProvider#getSelectedPage() + */ + public Object getSelectedPage() { + return getCurrentPage(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialog.IPageChangeProvider#addPageChangedListener() + */ + public void addPageChangedListener(IPageChangedListener listener) { + pageChangedListeners.add(listener); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialog.IPageChangeProvider#removePageChangedListener() + */ + public void removePageChangedListener(IPageChangedListener listener) { + pageChangedListeners.remove(listener); + } + + /** + * Notifies any selection changed listeners that the selected page has + * changed. Only listeners registered at the time this method is called are + * notified. + * + * @param event + * a selection changed event + * + * @see IPageChangedListener#pageChanged + * + * @since 3.1 + */ + protected void firePageChanged(final PageChangedEvent event) { + Object[] listeners = pageChangedListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + final IPageChangedListener l = (IPageChangedListener) listeners[i]; + SafeRunnable.run(new SafeRunnable() { + public void run() { + l.pageChanged(event); + } + }); + } + } + + /** + * Adds a listener for page changes to the list of page changing listeners + * registered for this dialog. Has no effect if an identical listener is + * already registered. + * + * @param listener + * a page changing listener + * @since 3.3 + */ + public void addPageChangingListener(IPageChangingListener listener) { + pageChangingListeners.add(listener); + } + + /** + * Removes the provided page changing listener from the list of page + * changing listeners registered for the dialog. + * + * @param listener + * a page changing listener + * @since 3.3 + */ + public void removePageChangingListener(IPageChangingListener listener) { + pageChangingListeners.remove(listener); + } + + /** + * Notifies any page changing listeners that the currently selected dialog + * page is changing. Only listeners registered at the time this method is + * called are notified. + * + * @param event + * a selection changing event + * + * @see IPageChangingListener#handlePageChanging(PageChangingEvent) + * @since 3.3 + */ + protected void firePageChanging(final PageChangingEvent event) { + Object[] listeners = pageChangingListeners.getListeners(); + for (int i = 0; i < listeners.length; ++i) { + final IPageChangingListener l = (IPageChangingListener) listeners[i]; + SafeRunnable.run(new SafeRunnable() { + public void run() { + l.handlePageChanging(event); + } + }); + } + } +} +++/ \ No newline at end of file