0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jface.dialogs;
0011:
0012: import java.util.Arrays;
0013: import java.util.HashMap;
0014:
0015: import org.eclipse.core.runtime.IProgressMonitor;
0016: import org.eclipse.core.runtime.IStatus;
0017: import org.eclipse.core.runtime.Status;
0018: import org.eclipse.jface.resource.JFaceResources;
0019: import org.eclipse.jface.util.Policy;
0020: import org.eclipse.jface.window.IShellProvider;
0021: import org.eclipse.jface.window.SameShellProvider;
0022: import org.eclipse.jface.window.Window;
0023: import org.eclipse.swt.SWT;
0024: import org.eclipse.swt.events.SelectionAdapter;
0025: import org.eclipse.swt.events.SelectionEvent;
0026: import org.eclipse.swt.graphics.Font;
0027: import org.eclipse.swt.graphics.FontData;
0028: import org.eclipse.swt.graphics.FontMetrics;
0029: import org.eclipse.swt.graphics.GC;
0030: import org.eclipse.swt.graphics.Image;
0031: import org.eclipse.swt.graphics.Point;
0032: import org.eclipse.swt.layout.FormData;
0033: import org.eclipse.swt.layout.GridData;
0034: import org.eclipse.swt.layout.GridLayout;
0035: import org.eclipse.swt.widgets.Button;
0036: import org.eclipse.swt.widgets.Composite;
0037: import org.eclipse.swt.widgets.Control;
0038: import org.eclipse.swt.widgets.Display;
0039: import org.eclipse.swt.widgets.Shell;
0040:
0041: /**
0042: * A dialog is a specialized window used for narrow-focused communication with
0043: * the user.
0044: * <p>
0045: * Dialogs are usually modal. Consequently, it is generally bad practice to open
0046: * a dialog without a parent. A modal dialog without a parent is not prevented
0047: * from disappearing behind the application's other windows, making it very
0048: * confusing for the user.
0049: * </p>
0050: * <p>
0051: * If there is more than one modal dialog is open the second one should be
0052: * parented off of the shell of the first one otherwise it is possible that the
0053: * OS will give focus to the first dialog potentially blocking the UI.
0054: * </p>
0055: */
0056: public abstract class Dialog extends Window {
0057: /**
0058: * Image registry key for error image (value
0059: * <code>"dialog_error_image"</code>).
0060: *
0061: * @deprecated use
0062: * org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_ERROR)
0063: */
0064: public static final String DLG_IMG_ERROR = "dialog_error_image"; //$NON-NLS-1$
0065:
0066: /**
0067: * Image registry key for info image (value <code>"dialog_info_image"</code>).
0068: *
0069: * @deprecated use
0070: * org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_INFORMATION)
0071: */
0072: public static final String DLG_IMG_INFO = "dialog_info_imageg"; //$NON-NLS-1$
0073:
0074: /**
0075: * Image registry key for question image (value
0076: * <code>"dialog_question_image"</code>).
0077: *
0078: * @deprecated org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_QUESTION)
0079: */
0080: public static final String DLG_IMG_QUESTION = "dialog_question_image"; //$NON-NLS-1$
0081:
0082: /**
0083: * Image registry key for warning image (value
0084: * <code>"dialog_warning_image"</code>).
0085: *
0086: * @deprecated use
0087: * org.eclipse.swt.widgets.Display.getSystemImage(SWT.ICON_WARNING)
0088: */
0089: public static final String DLG_IMG_WARNING = "dialog_warning_image"; //$NON-NLS-1$
0090:
0091: /**
0092: * Image registry key for info message image (value
0093: * <code>"dialog_messasge_info_image"</code>).
0094: *
0095: * @since 2.0
0096: */
0097: public static final String DLG_IMG_MESSAGE_INFO = "dialog_messasge_info_image"; //$NON-NLS-1$
0098:
0099: /**
0100: * Image registry key for info message image (value
0101: * <code>"dialog_messasge_warning_image"</code>).
0102: *
0103: * @since 2.0
0104: */
0105: public static final String DLG_IMG_MESSAGE_WARNING = "dialog_messasge_warning_image"; //$NON-NLS-1$
0106:
0107: /**
0108: * Image registry key for info message image (value
0109: * <code>"dialog_message_error_image"</code>).
0110: *
0111: * @since 2.0
0112: */
0113: public static final String DLG_IMG_MESSAGE_ERROR = "dialog_message_error_image"; //$NON-NLS-1$
0114:
0115: /**
0116: * Image registry key for help image (value
0117: * <code>"dialog_help_image"</code>).
0118: *
0119: * @since 3.2
0120: */
0121: public static final String DLG_IMG_HELP = "dialog_help_image"; //$NON-NLS-1$
0122:
0123: /**
0124: * The ellipsis is the string that is used to represent shortened text.
0125: *
0126: * @since 3.0
0127: */
0128: public static final String ELLIPSIS = "..."; //$NON-NLS-1$
0129:
0130: /**
0131: * The dialog settings key name for stored dialog x location.
0132: *
0133: * @since 3.2
0134: */
0135: private static final String DIALOG_ORIGIN_X = "DIALOG_X_ORIGIN"; //$NON-NLS-1$
0136:
0137: /**
0138: * The dialog settings key name for stored dialog y location.
0139: *
0140: * @since 3.2
0141: */
0142: private static final String DIALOG_ORIGIN_Y = "DIALOG_Y_ORIGIN"; //$NON-NLS-1$
0143:
0144: /**
0145: * The dialog settings key name for stored dialog width.
0146: *
0147: * @since 3.2
0148: */
0149: private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
0150:
0151: /**
0152: * The dialog settings key name for stored dialog height.
0153: *
0154: * @since 3.2
0155: */
0156: private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
0157:
0158: /**
0159: * The dialog settings key name for the font used when the dialog
0160: * height and width was stored.
0161: *
0162: *@since 3.2
0163: */
0164: private static final String DIALOG_FONT_DATA = "DIALOG_FONT_NAME"; //$NON-NLS-1$
0165:
0166: /**
0167: * A value that can be used for stored dialog width or height that
0168: * indicates that the default bounds should be used.
0169: *
0170: * @since 3.2
0171: */
0172: public static final int DIALOG_DEFAULT_BOUNDS = -1;
0173:
0174: /**
0175: * Constants that can be used for specifying the strategy for persisting
0176: * dialog bounds. These constants represent bit masks that can be used
0177: * together.
0178: *
0179: *@since 3.2
0180: */
0181:
0182: /**
0183: * Persist the last location of the dialog.
0184: * @since 3.2
0185: */
0186: public static final int DIALOG_PERSISTLOCATION = 0x0001;
0187: /**
0188: * Persist the last known size of the dialog.
0189: * @since 3.2
0190: */
0191: public static final int DIALOG_PERSISTSIZE = 0x0002;
0192:
0193: /**
0194: * The dialog area; <code>null</code> until dialog is layed out.
0195: */
0196: protected Control dialogArea;
0197:
0198: /**
0199: * The button bar; <code>null</code> until dialog is layed out.
0200: */
0201: public Control buttonBar;
0202:
0203: /**
0204: * Collection of buttons created by the <code>createButton</code> method.
0205: */
0206: private HashMap buttons = new HashMap();
0207:
0208: /**
0209: * Font metrics to use for determining pixel sizes.
0210: */
0211: private FontMetrics fontMetrics;
0212:
0213: /**
0214: * Number of horizontal dialog units per character, value <code>4</code>.
0215: */
0216: private static final int HORIZONTAL_DIALOG_UNIT_PER_CHAR = 4;
0217:
0218: /**
0219: * Number of vertical dialog units per character, value <code>8</code>.
0220: */
0221: private static final int VERTICAL_DIALOG_UNITS_PER_CHAR = 8;
0222:
0223: /**
0224: * Returns the number of pixels corresponding to the height of the given
0225: * number of characters.
0226: * <p>
0227: * The required <code>FontMetrics</code> parameter may be created in the
0228: * following way: <code>
0229: * GC gc = new GC(control);
0230: * gc.setFont(control.getFont());
0231: * fontMetrics = gc.getFontMetrics();
0232: * gc.dispose();
0233: * </code>
0234: * </p>
0235: *
0236: * @param fontMetrics
0237: * used in performing the conversion
0238: * @param chars
0239: * the number of characters
0240: * @return the number of pixels
0241: * @since 2.0
0242: */
0243: public static int convertHeightInCharsToPixels(
0244: FontMetrics fontMetrics, int chars) {
0245: return fontMetrics.getHeight() * chars;
0246: }
0247:
0248: /**
0249: * Returns the number of pixels corresponding to the given number of
0250: * horizontal dialog units.
0251: * <p>
0252: * The required <code>FontMetrics</code> parameter may be created in the
0253: * following way: <code>
0254: * GC gc = new GC(control);
0255: * gc.setFont(control.getFont());
0256: * fontMetrics = gc.getFontMetrics();
0257: * gc.dispose();
0258: * </code>
0259: * </p>
0260: *
0261: * @param fontMetrics
0262: * used in performing the conversion
0263: * @param dlus
0264: * the number of horizontal dialog units
0265: * @return the number of pixels
0266: * @since 2.0
0267: */
0268: public static int convertHorizontalDLUsToPixels(
0269: FontMetrics fontMetrics, int dlus) {
0270: // round to the nearest pixel
0271: return (fontMetrics.getAverageCharWidth() * dlus + HORIZONTAL_DIALOG_UNIT_PER_CHAR / 2)
0272: / HORIZONTAL_DIALOG_UNIT_PER_CHAR;
0273: }
0274:
0275: /**
0276: * Returns the number of pixels corresponding to the given number of
0277: * vertical dialog units.
0278: * <p>
0279: * The required <code>FontMetrics</code> parameter may be created in the
0280: * following way: <code>
0281: * GC gc = new GC(control);
0282: * gc.setFont(control.getFont());
0283: * fontMetrics = gc.getFontMetrics();
0284: * gc.dispose();
0285: * </code>
0286: * </p>
0287: *
0288: * @param fontMetrics
0289: * used in performing the conversion
0290: * @param dlus
0291: * the number of vertical dialog units
0292: * @return the number of pixels
0293: * @since 2.0
0294: */
0295: public static int convertVerticalDLUsToPixels(
0296: FontMetrics fontMetrics, int dlus) {
0297: // round to the nearest pixel
0298: return (fontMetrics.getHeight() * dlus + VERTICAL_DIALOG_UNITS_PER_CHAR / 2)
0299: / VERTICAL_DIALOG_UNITS_PER_CHAR;
0300: }
0301:
0302: /**
0303: * Returns the number of pixels corresponding to the width of the given
0304: * number of characters.
0305: * <p>
0306: * The required <code>FontMetrics</code> parameter may be created in the
0307: * following way: <code>
0308: * GC gc = new GC(control);
0309: * gc.setFont(control.getFont());
0310: * fontMetrics = gc.getFontMetrics();
0311: * gc.dispose();
0312: * </code>
0313: * </p>
0314: *
0315: * @param fontMetrics
0316: * used in performing the conversion
0317: * @param chars
0318: * the number of characters
0319: * @return the number of pixels
0320: * @since 2.0
0321: */
0322: public static int convertWidthInCharsToPixels(
0323: FontMetrics fontMetrics, int chars) {
0324: return fontMetrics.getAverageCharWidth() * chars;
0325: }
0326:
0327: /**
0328: * Shortens the given text <code>textValue</code> so that its width in
0329: * pixels does not exceed the width of the given control. Overrides
0330: * characters in the center of the original string with an ellipsis ("...")
0331: * if necessary. If a <code>null</code> value is given, <code>null</code>
0332: * is returned.
0333: *
0334: * @param textValue
0335: * the original string or <code>null</code>
0336: * @param control
0337: * the control the string will be displayed on
0338: * @return the string to display, or <code>null</code> if null was passed
0339: * in
0340: *
0341: * @since 3.0
0342: */
0343: public static String shortenText(String textValue, Control control) {
0344: if (textValue == null) {
0345: return null;
0346: }
0347: GC gc = new GC(control);
0348: int maxWidth = control.getBounds().width - 5;
0349: if (gc.textExtent(textValue).x < maxWidth) {
0350: gc.dispose();
0351: return textValue;
0352: }
0353: int length = textValue.length();
0354: int pivot = length / 2;
0355: int start = pivot;
0356: int end = pivot + 1;
0357: while (start >= 0 && end < length) {
0358: String s1 = textValue.substring(0, start);
0359: String s2 = textValue.substring(end, length);
0360: String s = s1 + ELLIPSIS + s2;
0361: int l = gc.textExtent(s).x;
0362: if (l < maxWidth) {
0363: gc.dispose();
0364: return s;
0365: }
0366: start--;
0367: end++;
0368: }
0369: gc.dispose();
0370: return textValue;
0371: }
0372:
0373: /**
0374: * Create a default instance of the blocked handler which does not do
0375: * anything.
0376: */
0377: public static IDialogBlockedHandler blockedHandler = new IDialogBlockedHandler() {
0378: /*
0379: * (non-Javadoc)
0380: *
0381: * @see org.eclipse.jface.dialogs.IDialogBlockedHandler#clearBlocked()
0382: */
0383: public void clearBlocked() {
0384: // No default behaviour
0385: }
0386:
0387: /*
0388: * (non-Javadoc)
0389: *
0390: * @see org.eclipse.jface.dialogs.IDialogBlockedHandler#showBlocked(org.eclipse.core.runtime.IProgressMonitor,
0391: * org.eclipse.core.runtime.IStatus, java.lang.String)
0392: */
0393: public void showBlocked(IProgressMonitor blocking,
0394: IStatus blockingStatus, String blockedName) {
0395: // No default behaviour
0396: }
0397:
0398: /*
0399: * (non-Javadoc)
0400: *
0401: * @see org.eclipse.jface.dialogs.IDialogBlockedHandler#showBlocked(org.eclipse.swt.widgets.Shell,
0402: * org.eclipse.core.runtime.IProgressMonitor,
0403: * org.eclipse.core.runtime.IStatus, java.lang.String)
0404: */
0405: public void showBlocked(Shell parentShell,
0406: IProgressMonitor blocking, IStatus blockingStatus,
0407: String blockedName) {
0408: // No default behaviour
0409: }
0410: };
0411:
0412: /**
0413: * Creates a dialog instance. Note that the window will have no visual
0414: * representation (no widgets) until it is told to open. By default,
0415: * <code>open</code> blocks for dialogs.
0416: *
0417: * @param parentShell
0418: * the parent shell, or <code>null</code> to create a top-level
0419: * shell
0420: */
0421: protected Dialog(Shell parentShell) {
0422: this (new SameShellProvider(parentShell));
0423: if (parentShell == null && Policy.DEBUG_DIALOG_NO_PARENT) {
0424: Policy.getLog().log(
0425: new Status(IStatus.INFO, Policy.JFACE,
0426: IStatus.INFO, this .getClass()
0427: + " created with no shell",//$NON-NLS-1$
0428: new Exception()));
0429: }
0430: }
0431:
0432: /**
0433: * Creates a dialog with the given parent.
0434: *
0435: * @param parentShell
0436: * object that returns the current parent shell
0437: *
0438: * @since 3.1
0439: */
0440: protected Dialog(IShellProvider parentShell) {
0441: super (parentShell);
0442: if (isResizable()) {
0443: setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL
0444: | SWT.MAX | SWT.RESIZE | getDefaultOrientation());
0445: } else {
0446: setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL
0447: | getDefaultOrientation());
0448: }
0449: setBlockOnOpen(true);
0450: }
0451:
0452: /**
0453: * Notifies that this dialog's button with the given id has been pressed.
0454: * <p>
0455: * The <code>Dialog</code> implementation of this framework method calls
0456: * <code>okPressed</code> if the ok button is the pressed, and
0457: * <code>cancelPressed</code> if the cancel button is the pressed. All
0458: * other button presses are ignored. Subclasses may override to handle other
0459: * buttons, but should call <code>super.buttonPressed</code> if the
0460: * default handling of the ok and cancel buttons is desired.
0461: * </p>
0462: *
0463: * @param buttonId
0464: * the id of the button that was pressed (see
0465: * <code>IDialogConstants.*_ID</code> constants)
0466: */
0467: protected void buttonPressed(int buttonId) {
0468: if (IDialogConstants.OK_ID == buttonId) {
0469: okPressed();
0470: } else if (IDialogConstants.CANCEL_ID == buttonId) {
0471: cancelPressed();
0472: }
0473: }
0474:
0475: /**
0476: * Notifies that the cancel button of this dialog has been pressed.
0477: * <p>
0478: * The <code>Dialog</code> implementation of this framework method sets
0479: * this dialog's return code to <code>Window.CANCEL</code> and closes the
0480: * dialog. Subclasses may override if desired.
0481: * </p>
0482: */
0483: protected void cancelPressed() {
0484: setReturnCode(CANCEL);
0485: close();
0486: }
0487:
0488: /**
0489: * Returns the number of pixels corresponding to the height of the given
0490: * number of characters.
0491: * <p>
0492: * This method may only be called after <code>initializeDialogUnits</code>
0493: * has been called.
0494: * </p>
0495: * <p>
0496: * Clients may call this framework method, but should not override it.
0497: * </p>
0498: *
0499: * @param chars
0500: * the number of characters
0501: * @return the number of pixels
0502: */
0503: protected int convertHeightInCharsToPixels(int chars) {
0504: // test for failure to initialize for backward compatibility
0505: if (fontMetrics == null) {
0506: return 0;
0507: }
0508: return convertHeightInCharsToPixels(fontMetrics, chars);
0509: }
0510:
0511: /**
0512: * Returns the number of pixels corresponding to the given number of
0513: * horizontal dialog units.
0514: * <p>
0515: * This method may only be called after <code>initializeDialogUnits</code>
0516: * has been called.
0517: * </p>
0518: * <p>
0519: * Clients may call this framework method, but should not override it.
0520: * </p>
0521: *
0522: * @param dlus
0523: * the number of horizontal dialog units
0524: * @return the number of pixels
0525: */
0526: protected int convertHorizontalDLUsToPixels(int dlus) {
0527: // test for failure to initialize for backward compatibility
0528: if (fontMetrics == null) {
0529: return 0;
0530: }
0531: return convertHorizontalDLUsToPixels(fontMetrics, dlus);
0532: }
0533:
0534: /**
0535: * Returns the number of pixels corresponding to the given number of
0536: * vertical dialog units.
0537: * <p>
0538: * This method may only be called after <code>initializeDialogUnits</code>
0539: * has been called.
0540: * </p>
0541: * <p>
0542: * Clients may call this framework method, but should not override it.
0543: * </p>
0544: *
0545: * @param dlus
0546: * the number of vertical dialog units
0547: * @return the number of pixels
0548: */
0549: protected int convertVerticalDLUsToPixels(int dlus) {
0550: // test for failure to initialize for backward compatibility
0551: if (fontMetrics == null) {
0552: return 0;
0553: }
0554: return convertVerticalDLUsToPixels(fontMetrics, dlus);
0555: }
0556:
0557: /**
0558: * Returns the number of pixels corresponding to the width of the given
0559: * number of characters.
0560: * <p>
0561: * This method may only be called after <code>initializeDialogUnits</code>
0562: * has been called.
0563: * </p>
0564: * <p>
0565: * Clients may call this framework method, but should not override it.
0566: * </p>
0567: *
0568: * @param chars
0569: * the number of characters
0570: * @return the number of pixels
0571: */
0572: protected int convertWidthInCharsToPixels(int chars) {
0573: // test for failure to initialize for backward compatibility
0574: if (fontMetrics == null) {
0575: return 0;
0576: }
0577: return convertWidthInCharsToPixels(fontMetrics, chars);
0578: }
0579:
0580: /**
0581: * Creates a new button with the given id.
0582: * <p>
0583: * The <code>Dialog</code> implementation of this framework method creates
0584: * a standard push button, registers it for selection events including
0585: * button presses, and registers default buttons with its shell. The button
0586: * id is stored as the button's client data. If the button id is
0587: * <code>IDialogConstants.CANCEL_ID</code>, the new button will be
0588: * accessible from <code>getCancelButton()</code>. If the button id is
0589: * <code>IDialogConstants.OK_ID</code>, the new button will be accesible
0590: * from <code>getOKButton()</code>. Note that the parent's layout is
0591: * assumed to be a <code>GridLayout</code> and the number of columns in
0592: * this layout is incremented. Subclasses may override.
0593: * </p>
0594: *
0595: * @param parent
0596: * the parent composite
0597: * @param id
0598: * the id of the button (see <code>IDialogConstants.*_ID</code>
0599: * constants for standard dialog button ids)
0600: * @param label
0601: * the label from the button
0602: * @param defaultButton
0603: * <code>true</code> if the button is to be the default button,
0604: * and <code>false</code> otherwise
0605: *
0606: * @return the new button
0607: *
0608: * @see #getCancelButton
0609: * @see #getOKButton()
0610: */
0611: protected Button createButton(Composite parent, int id,
0612: String label, boolean defaultButton) {
0613: // increment the number of columns in the button bar
0614: ((GridLayout) parent.getLayout()).numColumns++;
0615: Button button = new Button(parent, SWT.PUSH);
0616: button.setText(label);
0617: button.setFont(JFaceResources.getDialogFont());
0618: button.setData(new Integer(id));
0619: button.addSelectionListener(new SelectionAdapter() {
0620: public void widgetSelected(SelectionEvent event) {
0621: buttonPressed(((Integer) event.widget.getData())
0622: .intValue());
0623: }
0624: });
0625: if (defaultButton) {
0626: Shell shell = parent.getShell();
0627: if (shell != null) {
0628: shell.setDefaultButton(button);
0629: }
0630: }
0631: buttons.put(new Integer(id), button);
0632: setButtonLayoutData(button);
0633: return button;
0634: }
0635:
0636: /**
0637: * Creates and returns the contents of this dialog's button bar.
0638: * <p>
0639: * The <code>Dialog</code> implementation of this framework method lays
0640: * out a button bar and calls the <code>createButtonsForButtonBar</code>
0641: * framework method to populate it. Subclasses may override.
0642: * </p>
0643: * <p>
0644: * The returned control's layout data must be an instance of
0645: * <code>GridData</code>.
0646: * </p>
0647: *
0648: * @param parent
0649: * the parent composite to contain the button bar
0650: * @return the button bar control
0651: */
0652: protected Control createButtonBar(Composite parent) {
0653: Composite composite = new Composite(parent, SWT.NONE);
0654: // create a layout with spacing and margins appropriate for the font
0655: // size.
0656: GridLayout layout = new GridLayout();
0657: layout.numColumns = 0; // this is incremented by createButton
0658: layout.makeColumnsEqualWidth = true;
0659: layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
0660: layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
0661: layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
0662: layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
0663: composite.setLayout(layout);
0664: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END
0665: | GridData.VERTICAL_ALIGN_CENTER);
0666: composite.setLayoutData(data);
0667: composite.setFont(parent.getFont());
0668:
0669: // Add the buttons to the button bar.
0670: createButtonsForButtonBar(composite);
0671: return composite;
0672: }
0673:
0674: /**
0675: * Adds buttons to this dialog's button bar.
0676: * <p>
0677: * The <code>Dialog</code> implementation of this framework method adds
0678: * standard ok and cancel buttons using the <code>createButton</code>
0679: * framework method. These standard buttons will be accessible from
0680: * <code>getCancelButton</code>, and <code>getOKButton</code>.
0681: * Subclasses may override.
0682: * </p>
0683: *
0684: * @param parent
0685: * the button bar composite
0686: */
0687: protected void createButtonsForButtonBar(Composite parent) {
0688: // create OK and Cancel buttons by default
0689: createButton(parent, IDialogConstants.OK_ID,
0690: IDialogConstants.OK_LABEL, true);
0691: createButton(parent, IDialogConstants.CANCEL_ID,
0692: IDialogConstants.CANCEL_LABEL, false);
0693: }
0694:
0695: /*
0696: * @see Window.initializeBounds()
0697: */
0698: protected void initializeBounds() {
0699: String platform = SWT.getPlatform();
0700: if ("carbon".equals(platform)) { //$NON-NLS-1$
0701: // On Mac OS X the default button must be the right-most button
0702: Shell shell = getShell();
0703: if (shell != null) {
0704: Button defaultButton = shell.getDefaultButton();
0705: if (defaultButton != null
0706: && isContained(buttonBar, defaultButton)) {
0707: defaultButton.moveBelow(null);
0708: }
0709: }
0710: }
0711:
0712: super .initializeBounds();
0713: }
0714:
0715: /**
0716: * Returns true if the given Control is a direct or indirect child of
0717: * container.
0718: *
0719: * @param container
0720: * the potential parent
0721: * @param control
0722: * @return boolean <code>true</code> if control is a child of container
0723: */
0724: private boolean isContained(Control container, Control control) {
0725: Composite parent;
0726: while ((parent = control.getParent()) != null) {
0727: if (parent == container) {
0728: return true;
0729: }
0730: control = parent;
0731: }
0732: return false;
0733: }
0734:
0735: /**
0736: * The <code>Dialog</code> implementation of this <code>Window</code>
0737: * method creates and lays out the top level composite for the dialog, and
0738: * determines the appropriate horizontal and vertical dialog units based on
0739: * the font size. It then calls the <code>createDialogArea</code> and
0740: * <code>createButtonBar</code> methods to create the dialog area and
0741: * button bar, respectively. Overriding <code>createDialogArea</code> and
0742: * <code>createButtonBar</code> are recommended rather than overriding
0743: * this method.
0744: */
0745: protected Control createContents(Composite parent) {
0746: // create the top level composite for the dialog
0747: Composite composite = new Composite(parent, 0);
0748: GridLayout layout = new GridLayout();
0749: layout.marginHeight = 0;
0750: layout.marginWidth = 0;
0751: layout.verticalSpacing = 0;
0752: composite.setLayout(layout);
0753: composite.setLayoutData(new GridData(GridData.FILL_BOTH));
0754: applyDialogFont(composite);
0755: // initialize the dialog units
0756: initializeDialogUnits(composite);
0757: // create the dialog area and button bar
0758: dialogArea = createDialogArea(composite);
0759: buttonBar = createButtonBar(composite);
0760:
0761: return composite;
0762: }
0763:
0764: /**
0765: * Creates and returns the contents of the upper part of this dialog (above
0766: * the button bar).
0767: * <p>
0768: * The <code>Dialog</code> implementation of this framework method creates
0769: * and returns a new <code>Composite</code> with standard margins and
0770: * spacing.
0771: * </p>
0772: * <p>
0773: * The returned control's layout data must be an instance of
0774: * <code>GridData</code>. This method must not modify the parent's
0775: * layout.
0776: * </p>
0777: * <p>
0778: * Subclasses must override this method but may call <code>super</code> as
0779: * in the following example:
0780: * </p>
0781: *
0782: * <pre>
0783: * Composite composite = (Composite) super.createDialogArea(parent);
0784: * //add controls to composite as necessary
0785: * return composite;
0786: * </pre>
0787: *
0788: * @param parent
0789: * the parent composite to contain the dialog area
0790: * @return the dialog area control
0791: */
0792: protected Control createDialogArea(Composite parent) {
0793: // create a composite with standard margins and spacing
0794: Composite composite = new Composite(parent, SWT.NONE);
0795: GridLayout layout = new GridLayout();
0796: layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
0797: layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
0798: layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
0799: layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
0800: composite.setLayout(layout);
0801: composite.setLayoutData(new GridData(GridData.FILL_BOTH));
0802: applyDialogFont(composite);
0803: return composite;
0804: }
0805:
0806: /**
0807: * Returns the button created by the method <code>createButton</code> for
0808: * the specified ID as defined on <code>IDialogConstants</code>. If
0809: * <code>createButton</code> was never called with this ID, or if
0810: * <code>createButton</code> is overridden, this method will return
0811: * <code>null</code>.
0812: *
0813: * @param id
0814: * the id of the button to look for
0815: *
0816: * @return the button for the ID or <code>null</code>
0817: *
0818: * @see #createButton(Composite, int, String, boolean)
0819: * @since 2.0
0820: */
0821: protected Button getButton(int id) {
0822: return (Button) buttons.get(new Integer(id));
0823: }
0824:
0825: /**
0826: * Returns the button bar control.
0827: * <p>
0828: * Clients may call this framework method, but should not override it.
0829: * </p>
0830: *
0831: * @return the button bar, or <code>null</code> if the button bar has not
0832: * been created yet
0833: */
0834: protected Control getButtonBar() {
0835: return buttonBar;
0836: }
0837:
0838: /**
0839: * Returns the button created when <code>createButton</code> is called
0840: * with an ID of <code>IDialogConstants.CANCEL_ID</code>. If
0841: * <code>createButton</code> was never called with this parameter, or if
0842: * <code>createButton</code> is overridden, <code>getCancelButton</code>
0843: * will return <code>null</code>.
0844: *
0845: * @return the cancel button or <code>null</code>
0846: *
0847: * @see #createButton(Composite, int, String, boolean)
0848: * @since 2.0
0849: * @deprecated Use <code>getButton(IDialogConstants.CANCEL_ID)</code>
0850: * instead. This method will be removed soon.
0851: */
0852: protected Button getCancelButton() {
0853: return getButton(IDialogConstants.CANCEL_ID);
0854: }
0855:
0856: /**
0857: * Returns the dialog area control.
0858: * <p>
0859: * Clients may call this framework method, but should not override it.
0860: * </p>
0861: *
0862: * @return the dialog area, or <code>null</code> if the dialog area has
0863: * not been created yet
0864: */
0865: protected Control getDialogArea() {
0866: return dialogArea;
0867: }
0868:
0869: /**
0870: * Returns the standard dialog image with the given key. Note that these
0871: * images are managed by the dialog framework, and must not be disposed by
0872: * another party.
0873: *
0874: * @param key
0875: * one of the <code>Dialog.DLG_IMG_* </code> constants
0876: * @return the standard dialog image
0877: *
0878: * NOTE: Dialog does not use the following images in the registry
0879: * DLG_IMG_ERROR DLG_IMG_INFO DLG_IMG_QUESTION DLG_IMG_WARNING
0880: *
0881: * They are now coming directly from SWT, see ImageRegistry. For backwards
0882: * compatibility they are still supported, however new code should use SWT
0883: * for these.
0884: *
0885: * @see Display#getSystemImage(int)
0886: */
0887: public static Image getImage(String key) {
0888: return JFaceResources.getImageRegistry().get(key);
0889: }
0890:
0891: /**
0892: * Returns the button created when <code>createButton</code> is called
0893: * with an ID of <code>IDialogConstants.OK_ID</code>. If
0894: * <code>createButton</code> was never called with this parameter, or if
0895: * <code>createButton</code> is overridden, <code>getOKButton</code>
0896: * will return <code>null</code>.
0897: *
0898: * @return the OK button or <code>null</code>
0899: *
0900: * @see #createButton(Composite, int, String, boolean)
0901: * @since 2.0
0902: * @deprecated Use <code>getButton(IDialogConstants.OK_ID)</code> instead.
0903: * This method will be removed soon.
0904: */
0905: protected Button getOKButton() {
0906: return getButton(IDialogConstants.OK_ID);
0907: }
0908:
0909: /**
0910: * Initializes the computation of horizontal and vertical dialog units based
0911: * on the size of current font.
0912: * <p>
0913: * This method must be called before any of the dialog unit based conversion
0914: * methods are called.
0915: * </p>
0916: *
0917: * @param control
0918: * a control from which to obtain the current font
0919: */
0920: protected void initializeDialogUnits(Control control) {
0921: // Compute and store a font metric
0922: GC gc = new GC(control);
0923: gc.setFont(JFaceResources.getDialogFont());
0924: fontMetrics = gc.getFontMetrics();
0925: gc.dispose();
0926: }
0927:
0928: /**
0929: * Notifies that the ok button of this dialog has been pressed.
0930: * <p>
0931: * The <code>Dialog</code> implementation of this framework method sets
0932: * this dialog's return code to <code>Window.OK</code> and closes the
0933: * dialog. Subclasses may override.
0934: * </p>
0935: */
0936: protected void okPressed() {
0937: setReturnCode(OK);
0938: close();
0939: }
0940:
0941: /**
0942: * Set the layout data of the button to a GridData with appropriate heights
0943: * and widths.
0944: *
0945: * @param button
0946: */
0947: protected void setButtonLayoutData(Button button) {
0948: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
0949: int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
0950: Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT,
0951: true);
0952: data.widthHint = Math.max(widthHint, minSize.x);
0953: button.setLayoutData(data);
0954: }
0955:
0956: /**
0957: * Set the layout data of the button to a FormData with appropriate heights
0958: * and widths.
0959: *
0960: * @param button
0961: */
0962: protected void setButtonLayoutFormData(Button button) {
0963: FormData data = new FormData();
0964: int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
0965: Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT,
0966: true);
0967: data.width = Math.max(widthHint, minSize.x);
0968: button.setLayoutData(data);
0969: }
0970:
0971: /**
0972: * @see org.eclipse.jface.window.Window#close()
0973: */
0974: public boolean close() {
0975: if (getShell() != null && !getShell().isDisposed()) {
0976: saveDialogBounds(getShell());
0977: }
0978:
0979: boolean returnValue = super .close();
0980: if (returnValue) {
0981: buttons = new HashMap();
0982: buttonBar = null;
0983: dialogArea = null;
0984: }
0985: return returnValue;
0986: }
0987:
0988: /**
0989: * Applies the dialog font to all controls that currently have the default
0990: * font.
0991: *
0992: * @param control
0993: * the control to apply the font to. Font will also be applied to
0994: * its children. If the control is <code>null</code> nothing
0995: * happens.
0996: */
0997: public static void applyDialogFont(Control control) {
0998: if (control == null || dialogFontIsDefault()) {
0999: return;
1000: }
1001: Font dialogFont = JFaceResources.getDialogFont();
1002: applyDialogFont(control, dialogFont);
1003: }
1004:
1005: /**
1006: * Sets the dialog font on the control and any of its children if thier font
1007: * is not otherwise set.
1008: *
1009: * @param control
1010: * the control to apply the font to. Font will also be applied to
1011: * its children.
1012: * @param dialogFont
1013: * the dialog font to set
1014: */
1015: private static void applyDialogFont(Control control, Font dialogFont) {
1016: if (hasDefaultFont(control)) {
1017: control.setFont(dialogFont);
1018: }
1019: if (control instanceof Composite) {
1020: Control[] children = ((Composite) control).getChildren();
1021: for (int i = 0; i < children.length; i++) {
1022: applyDialogFont(children[i], dialogFont);
1023: }
1024: }
1025: }
1026:
1027: /**
1028: * Return whether or not this control has the same font as it's default.
1029: *
1030: * @param control
1031: * Control
1032: * @return boolean
1033: */
1034: private static boolean hasDefaultFont(Control control) {
1035: FontData[] controlFontData = control.getFont().getFontData();
1036: FontData[] defaultFontData = getDefaultFont(control)
1037: .getFontData();
1038: if (controlFontData.length == defaultFontData.length) {
1039: for (int i = 0; i < controlFontData.length; i++) {
1040: if (controlFontData[i].equals(defaultFontData[i])) {
1041: continue;
1042: }
1043: return false;
1044: }
1045: return true;
1046: }
1047: return false;
1048: }
1049:
1050: /**
1051: * Get the default font for this type of control.
1052: *
1053: * @param control
1054: * @return the default font
1055: */
1056: private static Font getDefaultFont(Control control) {
1057: String fontName = "DEFAULT_FONT_" + control.getClass().getName(); //$NON-NLS-1$
1058: if (JFaceResources.getFontRegistry().hasValueFor(fontName)) {
1059: return JFaceResources.getFontRegistry().get(fontName);
1060: }
1061: Font cached = control.getFont();
1062: control.setFont(null);
1063: Font defaultFont = control.getFont();
1064: control.setFont(cached);
1065: JFaceResources.getFontRegistry().put(fontName,
1066: defaultFont.getFontData());
1067: return defaultFont;
1068: }
1069:
1070: /**
1071: * Return whether or not the dialog font is currently the same as the
1072: * default font.
1073: *
1074: * @return boolean if the two are the same
1075: */
1076: protected static boolean dialogFontIsDefault() {
1077: FontData[] dialogFontData = JFaceResources.getFontRegistry()
1078: .getFontData(JFaceResources.DIALOG_FONT);
1079: FontData[] defaultFontData = JFaceResources.getFontRegistry()
1080: .getFontData(JFaceResources.DEFAULT_FONT);
1081: return Arrays.equals(dialogFontData, defaultFontData);
1082: }
1083:
1084: /*
1085: * (non-Javadoc)
1086: *
1087: * @see org.eclipse.jface.window.Window#create()
1088: */
1089: public void create() {
1090: super .create();
1091: applyDialogFont(buttonBar);
1092: }
1093:
1094: /**
1095: * Get the IDialogBlockedHandler to be used by WizardDialogs and
1096: * ModalContexts.
1097: *
1098: * @return Returns the blockedHandler.
1099: */
1100: public static IDialogBlockedHandler getBlockedHandler() {
1101: return blockedHandler;
1102: }
1103:
1104: /**
1105: * Set the IDialogBlockedHandler to be used by WizardDialogs and
1106: * ModalContexts.
1107: *
1108: * @param blockedHandler
1109: * The blockedHandler for the dialogs.
1110: */
1111: public static void setBlockedHandler(
1112: IDialogBlockedHandler blockedHandler) {
1113: Dialog.blockedHandler = blockedHandler;
1114: }
1115:
1116: /**
1117: * Gets the dialog settings that should be used for remembering the bounds of
1118: * of the dialog, according to the dialog bounds strategy.
1119: *
1120: * @return settings the dialog settings used to store the dialog's location
1121: * and/or size, or <code>null</code> if the dialog's bounds should
1122: * never be stored.
1123: *
1124: * @since 3.2
1125: * @see Dialog#getDialogBoundsStrategy()
1126: */
1127: protected IDialogSettings getDialogBoundsSettings() {
1128: return null;
1129: }
1130:
1131: /**
1132: * Get the integer constant that describes the strategy for persisting the
1133: * dialog bounds. This strategy is ignored if the implementer does not also
1134: * specify the dialog settings for storing the bounds in
1135: * Dialog.getDialogBoundsSettings().
1136: *
1137: * @return the constant describing the strategy for persisting the dialog
1138: * bounds.
1139: *
1140: * @since 3.2
1141: * @see Dialog#DIALOG_PERSISTLOCATION
1142: * @see Dialog#DIALOG_PERSISTSIZE
1143: * @see Dialog#getDialogBoundsSettings()
1144: */
1145: protected int getDialogBoundsStrategy() {
1146: return DIALOG_PERSISTLOCATION | DIALOG_PERSISTSIZE;
1147: }
1148:
1149: /**
1150: * Saves the bounds of the shell in the appropriate dialog settings. The
1151: * bounds are recorded relative to the parent shell, if there is one, or
1152: * display coordinates if there is no parent shell.
1153: *
1154: * @param shell
1155: * The shell whose bounds are to be stored
1156: *
1157: * @since 3.2
1158: */
1159: private void saveDialogBounds(Shell shell) {
1160: IDialogSettings settings = getDialogBoundsSettings();
1161: if (settings != null) {
1162: Point shellLocation = shell.getLocation();
1163: Point shellSize = shell.getSize();
1164: Shell parent = getParentShell();
1165: if (parent != null) {
1166: Point parentLocation = parent.getLocation();
1167: shellLocation.x -= parentLocation.x;
1168: shellLocation.y -= parentLocation.y;
1169: }
1170: int strategy = getDialogBoundsStrategy();
1171: if ((strategy & DIALOG_PERSISTLOCATION) != 0) {
1172: settings.put(DIALOG_ORIGIN_X, shellLocation.x);
1173: settings.put(DIALOG_ORIGIN_Y, shellLocation.y);
1174: }
1175: if ((strategy & DIALOG_PERSISTSIZE) != 0) {
1176: settings.put(DIALOG_WIDTH, shellSize.x);
1177: settings.put(DIALOG_HEIGHT, shellSize.y);
1178: FontData[] fontDatas = JFaceResources.getDialogFont()
1179: .getFontData();
1180: if (fontDatas.length > 0) {
1181: settings.put(DIALOG_FONT_DATA, fontDatas[0]
1182: .toString());
1183: }
1184: }
1185: }
1186: }
1187:
1188: /**
1189: * Returns the initial size to use for the shell. Overridden
1190: * to check whether a size has been stored in dialog settings.
1191: * If a size has been stored, it is returned.
1192: *
1193: * @return the initial size of the shell
1194: *
1195: * @since 3.2
1196: * @see #getDialogBoundsSettings()
1197: * @see #getDialogBoundsStrategy()
1198: */
1199: protected Point getInitialSize() {
1200: Point result = super .getInitialSize();
1201:
1202: // Check the dialog settings for a stored size.
1203: if ((getDialogBoundsStrategy() & DIALOG_PERSISTSIZE) != 0) {
1204: IDialogSettings settings = getDialogBoundsSettings();
1205: if (settings != null) {
1206: // Check that the dialog font matches the font used
1207: // when the bounds was stored. If the font has changed,
1208: // we do not honor the stored settings.
1209: // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132821
1210: boolean useStoredBounds = true;
1211: String previousDialogFontData = settings
1212: .get(DIALOG_FONT_DATA);
1213: // There is a previously stored font, so we will check it.
1214: // Note that if we haven't stored the font before, then we will
1215: // use the stored bounds. This allows restoring of dialog bounds
1216: // that were stored before we started storing the fontdata.
1217: if (previousDialogFontData != null
1218: && previousDialogFontData.length() > 0) {
1219: FontData[] fontDatas = JFaceResources
1220: .getDialogFont().getFontData();
1221: if (fontDatas.length > 0) {
1222: String currentDialogFontData = fontDatas[0]
1223: .toString();
1224: useStoredBounds = currentDialogFontData
1225: .equalsIgnoreCase(previousDialogFontData);
1226: }
1227: }
1228: if (useStoredBounds) {
1229: try {
1230: // Get the stored width and height.
1231: int width = settings.getInt(DIALOG_WIDTH);
1232: if (width != DIALOG_DEFAULT_BOUNDS) {
1233: result.x = width;
1234: }
1235: int height = settings.getInt(DIALOG_HEIGHT);
1236: if (height != DIALOG_DEFAULT_BOUNDS) {
1237: result.y = height;
1238: }
1239:
1240: } catch (NumberFormatException e) {
1241: }
1242: }
1243: }
1244: }
1245: // No attempt is made to constrain the bounds. The default
1246: // constraining behavior in Window will be used.
1247: return result;
1248: }
1249:
1250: /**
1251: * Returns the initial location to use for the shell. Overridden
1252: * to check whether the bounds of the dialog have been stored in
1253: * dialog settings. If a location has been stored, it is returned.
1254: *
1255: * @param initialSize
1256: * the initial size of the shell, as returned by
1257: * <code>getInitialSize</code>.
1258: * @return the initial location of the shell
1259: *
1260: * @since 3.2
1261: * @see #getDialogBoundsSettings()
1262: * @see #getDialogBoundsStrategy()
1263: */
1264: protected Point getInitialLocation(Point initialSize) {
1265: Point result = super .getInitialLocation(initialSize);
1266: if ((getDialogBoundsStrategy() & DIALOG_PERSISTLOCATION) != 0) {
1267: IDialogSettings settings = getDialogBoundsSettings();
1268: if (settings != null) {
1269: try {
1270: int x = settings.getInt(DIALOG_ORIGIN_X);
1271: int y = settings.getInt(DIALOG_ORIGIN_Y);
1272: result = new Point(x, y);
1273: // The coordinates were stored relative to the parent shell.
1274: // Convert to display coordinates.
1275: Shell parent = getParentShell();
1276: if (parent != null) {
1277: Point parentLocation = parent.getLocation();
1278: result.x += parentLocation.x;
1279: result.y += parentLocation.y;
1280: }
1281: } catch (NumberFormatException e) {
1282: }
1283: }
1284: }
1285: // No attempt is made to constrain the bounds. The default
1286: // constraining behavior in Window will be used.
1287: return result;
1288: }
1289:
1290: /**
1291: * Returns a boolean indicating whether the dialog should be
1292: * considered resizable when the shell style is initially
1293: * set.
1294: *
1295: * This method is used to ensure that all style
1296: * bits appropriate for resizable dialogs are added to the
1297: * shell style. Individual dialogs may always set the shell
1298: * style to ensure that a dialog is resizable, but using this
1299: * method ensures that resizable dialogs will be created with
1300: * the same set of style bits.
1301: *
1302: * Style bits will never be removed based on the return value
1303: * of this method. For example, if a dialog returns
1304: * <code>false</code>, but also sets a style bit for a
1305: * SWT.RESIZE border, the style bit will be honored.
1306: *
1307: * @return a boolean indicating whether the dialog is
1308: * resizable and should have the default style bits for
1309: * resizable dialogs
1310: *
1311: * @since 3.4
1312: */
1313: protected boolean isResizable() {
1314: return false;
1315: }
1316: }
|