# HG changeset patch # User Frank Benoit # Date 1206920512 -7200 # Node ID 6886832e1ed8b543e3018df34fe596efbc5915ba # Parent 8ec40848221ba07540437165d96abfa38344e787 ErrorDialog diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/dialogs/ErrorDialog.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/dialogs/ErrorDialog.d Mon Mar 31 01:41:52 2008 +0200 @@ -0,0 +1,684 @@ +/******************************************************************************* + * 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 + * Sebastian Davids - Fix for bug 19346 - Dialog font should + * be activated and used by other components. + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.dialogs.ErrorDialog; + +import dwtx.jface.dialogs.IconAndMessageDialog; +import dwtx.jface.dialogs.IDialogConstants; +import dwtx.jface.dialogs.ErrorSupportProvider; + +import dwt.DWT; +import dwt.dnd.Clipboard; +import dwt.dnd.TextTransfer; +import dwt.dnd.Transfer; +import dwt.events.SelectionEvent; +import dwt.events.SelectionListener; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.layout.GridData; +import dwt.layout.GridLayout; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Label; +import dwt.widgets.List; +import dwt.widgets.Menu; +import dwt.widgets.MenuItem; +import dwt.widgets.Shell; +import dwtx.core.runtime.CoreException; +import dwtx.core.runtime.IStatus; +import dwtx.jface.resource.JFaceResources; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +static import tango.text.Text; +alias tango.text.Text.Text!(char) StringBuffer; +/** + * A dialog to display one or more errors to the user, as contained in an + * IStatus object. If an error contains additional detailed + * information then a Details button is automatically supplied, which shows or + * hides an error details viewer when pressed by the user. + * + * @see dwtx.core.runtime.IStatus + */ +public class ErrorDialog : IconAndMessageDialog { + /** + * Static to prevent opening of error dialogs for automated testing. + */ + public static bool AUTOMATED_MODE = false; + + /** + * Reserve room for this many list items. + */ + private static const int LIST_ITEM_COUNT = 7; + + /** + * The nesting indent. + */ + private static const String NESTING_INDENT = " "; //$NON-NLS-1$ + + /** + * The Details button. + */ + private Button detailsButton; + + /** + * The title of the dialog. + */ + private String title; + + /** + * The DWT list control that displays the error details. + */ + private List list; + + /** + * Indicates whether the error details viewer is currently created. + */ + private bool listCreated = false; + + /** + * Filter mask for determining which status items to display. + */ + private int displayMask = 0xFFFF; + + /** + * The main status object. + */ + private IStatus status; + + /** + * The current clipboard. To be disposed when closing the dialog. + */ + private Clipboard clipboard; + + private bool shouldIncludeTopLevelErrorInDetails = false; + + + /** + * Creates an error dialog. Note that the dialog will have no visual + * representation (no widgets) until it is told to open. + *

+ * Normally one should use openError to create and open one + * of these. This constructor is useful only if the error object being + * displayed contains child items and you need to specify a mask + * which will be used to filter the displaying of these children. The error + * dialog will only be displayed if there is at least one child status + * matching the mask. + *

+ * + * @param parentShell + * the shell under which to create this dialog + * @param dialogTitle + * the title to use for this dialog, or null to + * indicate that the default title should be used + * @param message + * the message to show in this dialog, or null to + * indicate that the error's message should be shown as the + * primary message + * @param status + * the error to show to the user + * @param displayMask + * the mask to use to filter the displaying of child items, as + * per IStatus.matches + * @see dwtx.core.runtime.IStatus#matches(int) + */ + public this(Shell parentShell, String dialogTitle, String message, + IStatus status, int displayMask) { + super(parentShell); + this.title = dialogTitle is null ? JFaceResources + .getString("Problem_Occurred") : //$NON-NLS-1$ + dialogTitle; + this.message = message is null ? status.getMessage() + : JFaceResources + .format( + "Reason", [ message, status.getMessage() ]); //$NON-NLS-1$ + this.status = status; + this.displayMask = displayMask; + setShellStyle(getShellStyle() | DWT.RESIZE); + } + + /* + * (non-Javadoc) Method declared on Dialog. Handles the pressing of the Ok + * or Details button in this dialog. If the Ok button was pressed then close + * this dialog. If the Details button was pressed then toggle the displaying + * of the error details area. Note that the Details button will only be + * visible if the error being displayed specifies child details. + */ + protected void buttonPressed(int id) { + if (id is IDialogConstants.DETAILS_ID) { + // was the details button pressed? + toggleDetailsArea(); + } else { + super.buttonPressed(id); + } + } + + /* + * (non-Javadoc) Method declared in Window. + */ + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText(title); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.Dialog#createButtonsForButtonBar(dwt.widgets.Composite) + */ + protected void createButtonsForButtonBar(Composite parent) { + // create OK and Details buttons + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, + true); + createDetailsButton(parent); + } + + /** + * Create the area for extra error support information. + * + * @param parent + */ + private void createSupportArea(Composite parent) { + + ErrorSupportProvider provider = Policy.getErrorSupportProvider(); + + if (provider is null) + return; + + Composite supportArea = new Composite(parent, DWT.NONE); + provider.createSupportArea(supportArea, status); + + GridData supportData = new GridData(DWT.FILL, DWT.FILL, true, true); + supportData.verticalSpan = 3; + supportArea.setLayoutData(supportData); + if (supportArea.getLayout() is null){ + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + supportArea.setLayout(layout); // Give it a default layout if one isn't set + } + + + } + + /** + * Create the details button if it should be included. + * + * @param parent + * the parent composite + * @since 3.2 + */ + protected void createDetailsButton(Composite parent) { + if (shouldShowDetailsButton()) { + detailsButton = createButton(parent, IDialogConstants.DETAILS_ID, + IDialogConstants.SHOW_DETAILS_LABEL, false); + } + } + + /** + * This implementation of the Dialog framework method creates + * and lays out a composite. Subclasses that require a different dialog area + * may either override this method, or call the super + * implementation and add controls to the created composite. + */ + protected Control createDialogArea(Composite parent) { + createMessageArea(parent); + createSupportArea(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); + layout.numColumns = 2; + composite.setLayout(layout); + GridData childData = new GridData(GridData.FILL_BOTH); + childData.horizontalSpan = 2; + composite.setLayoutData(childData); + composite.setFont(parent.getFont()); + + return composite; + } + + /* + * @see IconAndMessageDialog#createDialogAndButtonArea(Composite) + */ + protected void createDialogAndButtonArea(Composite parent) { + super.createDialogAndButtonArea(parent); + if ( auto dialogComposite = cast(Composite)this.dialogArea ) { + // Create a label if there are no children to force a smaller layout + if (dialogComposite.getChildren().length is 0) { + new Label(dialogComposite, DWT.NULL); + } + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.IconAndMessageDialog#getImage() + */ + protected Image getImage() { + if (status !is null) { + if (status.getSeverity() is IStatus.WARNING) { + return getWarningImage(); + } + if (status.getSeverity() is IStatus.INFO) { + return getInfoImage(); + } + } + // If it was not a warning or an error then return the error image + return getErrorImage(); + } + + /** + * Create this dialog's drop-down list component. + * + * @param parent + * the parent composite + * @return the drop-down list component + */ + protected List createDropDownList(Composite parent) { + // create the list + list = new List(parent, DWT.BORDER | DWT.H_SCROLL | DWT.V_SCROLL + | DWT.MULTI); + // fill the list + populateList(list); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL + | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL + | GridData.GRAB_VERTICAL); + data.heightHint = list.getItemHeight() * LIST_ITEM_COUNT; + data.horizontalSpan = 2; + list.setLayoutData(data); + list.setFont(parent.getFont()); + Menu copyMenu = new Menu(list); + MenuItem copyItem = new MenuItem(copyMenu, DWT.NONE); + copyItem.addSelectionListener(new class SelectionListener { + /* + * @see SelectionListener.widgetSelected (SelectionEvent) + */ + public void widgetSelected(SelectionEvent e) { + copyToClipboard(); + } + + /* + * @see SelectionListener.widgetDefaultSelected(SelectionEvent) + */ + public void widgetDefaultSelected(SelectionEvent e) { + copyToClipboard(); + } + }); + copyItem.setText(JFaceResources.getString("copy")); //$NON-NLS-1$ + list.setMenu(copyMenu); + listCreated = true; + return list; + } + + /* + * (non-Javadoc) Method declared on Window. + */ + /** + * Extends Window.open(). Opens an error dialog to display + * the error. If you specified a mask to filter the displaying of these + * children, the error dialog will only be displayed if there is at least + * one child status matching the mask. + */ + public int open() { + if (!AUTOMATED_MODE && shouldDisplay(status, displayMask)) { + return super.open(); + } + setReturnCode(OK); + return OK; + } + + /** + * Opens an error dialog to display the given error. Use this method if the + * error object being displayed does not contain child items, or if you wish + * to display all such items without filtering. + * + * @param parent + * the parent shell of the dialog, or null if none + * @param dialogTitle + * the title to use for this dialog, or null to + * indicate that the default title should be used + * @param message + * the message to show in this dialog, or null to + * indicate that the error's message should be shown as the + * primary message + * @param status + * the error to show to the user + * @return the code of the button that was pressed that resulted in this + * dialog closing. This will be Dialog.OK if the OK + * button was pressed, or Dialog.CANCEL if this + * dialog's close window decoration or the ESC key was used. + */ + public static int openError(Shell parent, String dialogTitle, + String message, IStatus status) { + return openError(parent, dialogTitle, message, status, IStatus.OK + | IStatus.INFO | IStatus.WARNING | IStatus.ERROR); + } + + /** + * Opens an error dialog to display the given error. Use this method if the + * error object being displayed contains child items and you wish + * to specify a mask which will be used to filter the displaying of these + * children. The error dialog will only be displayed if there is at least + * one child status matching the mask. + * + * @param parentShell + * the parent shell of the dialog, or null if none + * @param title + * the title to use for this dialog, or null to + * indicate that the default title should be used + * @param message + * the message to show in this dialog, or null to + * indicate that the error's message should be shown as the + * primary message + * @param status + * the error to show to the user + * @param displayMask + * the mask to use to filter the displaying of child items, as + * per IStatus.matches + * @return the code of the button that was pressed that resulted in this + * dialog closing. This will be Dialog.OK if the OK + * button was pressed, or Dialog.CANCEL if this + * dialog's close window decoration or the ESC key was used. + * @see dwtx.core.runtime.IStatus#matches(int) + */ + public static int openError(Shell parentShell, String title, + String message, IStatus status, int displayMask) { + ErrorDialog dialog = new ErrorDialog(parentShell, title, message, + status, displayMask); + return dialog.open(); + } + + /** + * Populates the list using this error dialog's status object. This walks + * the child static of the status object and displays them in a list. The + * format for each entry is status_path : status_message If the status's + * path was null then it (and the colon) are omitted. + * + * @param listToPopulate + * The list to fill. + */ + private void populateList(List listToPopulate) { + populateList(listToPopulate, status, 0, + shouldIncludeTopLevelErrorInDetails); + } + + /** + * Populate the list with the messages from the given status. Traverse the + * children of the status deeply and also traverse CoreExceptions that + * appear in the status. + * + * @param listToPopulate + * the list to populate + * @param buildingStatus + * the status being displayed + * @param nesting + * the nesting level (increases one level for each level of + * children) + * @param includeStatus + * whether to include the buildingStatus in the display or just + * its children + */ + private void populateList(List listToPopulate, IStatus buildingStatus, + int nesting, bool includeStatus) { + + if (!buildingStatus.matches(displayMask)) { + return; + } + + Exception t = buildingStatus.getException(); + bool isCoreException = null !is cast(CoreException)t; + bool incrementNesting = false; + + if (includeStatus) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < nesting; i++) { + sb.append(NESTING_INDENT); + } + String message = buildingStatus.getMessage(); + sb.append(message); + listToPopulate.add(sb.toString()); + incrementNesting = true; + } + + if (!isCoreException && t !is null) { + // Include low-level exception message + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < nesting; i++) { + sb.append(NESTING_INDENT); + } + //TODO how about getLocalizedMessage? + //String message = t.getLocalizedMessage(); + String message = t.msg; + if (message is null) { + message = t.toString(); + } + + sb.append(message); + listToPopulate.add(sb.toString()); + incrementNesting = true; + } + + if (incrementNesting) { + nesting++; + } + + // Look for a nested core exception + if (isCoreException) { + CoreException ce = cast(CoreException) t; + IStatus eStatus = ce.getStatus(); + // Only print the exception message if it is not contained in the + // parent message + if (message is null || message.indexOf(eStatus.getMessage()) is -1) { + populateList(listToPopulate, eStatus, nesting, true); + } + } + + // Look for child status + IStatus[] children = buildingStatus.getChildren(); + for (int i = 0; i < children.length; i++) { + populateList(listToPopulate, children[i], nesting, true); + } + } + + /** + * Returns whether the given status object should be displayed. + * + * @param status + * a status object + * @param mask + * a mask as per IStatus.matches + * @return true if the given status should be displayed, and + * false otherwise + * @see dwtx.core.runtime.IStatus#matches(int) + */ + protected static bool shouldDisplay(IStatus status, int mask) { + IStatus[] children = status.getChildren(); + if (children is null || children.length is 0) { + return status.matches(mask); + } + for (int i = 0; i < children.length; i++) { + if (children[i].matches(mask)) { + return true; + } + } + return false; + } + + /** + * Toggles the unfolding of the details area. This is triggered by the user + * pressing the details button. + */ + private void toggleDetailsArea() { + Point windowSize = getShell().getSize(); + Point oldSize = getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT); + if (listCreated) { + list.dispose(); + listCreated = false; + detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL); + } else { + list = createDropDownList(cast(Composite) getContents()); + detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL); + } + Point newSize = getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT); + getShell() + .setSize( + new Point(windowSize.x, windowSize.y + + (newSize.y - oldSize.y))); + } + + /** + * Put the details of the status of the error onto the stream. + * + * @param buildingStatus + * @param buffer + * @param nesting + */ + private void populateCopyBuffer(IStatus buildingStatus, + StringBuffer buffer, int nesting) { + if (!buildingStatus.matches(displayMask)) { + return; + } + for (int i = 0; i < nesting; i++) { + buffer.append(NESTING_INDENT); + } + buffer.append(buildingStatus.getMessage()); + buffer.append("\n"); //$NON-NLS-1$ + + // Look for a nested core exception + Exception t = buildingStatus.getException(); + if ( auto ce = cast(CoreException)t ) { + populateCopyBuffer(ce.getStatus(), buffer, nesting + 1); + } else if (t !is null) { + // Include low-level exception message + for (int i = 0; i < nesting; i++) { + buffer.append(NESTING_INDENT); + } + //TODO: getLocalizedMessage? + String message = t.msg; + if (message is null) { + message = t.toString(); + } + buffer.append(message); + buffer.append("\n"); //$NON-NLS-1$ + } + + IStatus[] children = buildingStatus.getChildren(); + for (int i = 0; i < children.length; i++) { + populateCopyBuffer(children[i], buffer, nesting + 1); + } + } + + /** + * Copy the contents of the statuses to the clipboard. + */ + private void copyToClipboard() { + if (clipboard !is null) { + clipboard.dispose(); + } + StringBuffer statusBuffer = new StringBuffer(); + populateCopyBuffer(status, statusBuffer, 0); + clipboard = new Clipboard(list.getDisplay()); + clipboard.setContents([ new ArrayWrapperString(statusBuffer.toString()) ], + [ TextTransfer.getInstance() ]); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#close() + */ + public bool close() { + if (clipboard !is null) { + clipboard.dispose(); + } + return super.close(); + } + + /** + * Show the details portion of the dialog if it is not already visible. This + * method will only work when it is invoked after the control of the dialog + * has been set. In other words, after the createContents + * method has been invoked and has returned the control for the content area + * of the dialog. Invoking the method before the content area has been set + * or after the dialog has been disposed will have no effect. + * + * @since 3.1 + */ + protected final void showDetailsArea() { + if (!listCreated) { + Control control = getContents(); + if (control !is null && !control.isDisposed()) { + toggleDetailsArea(); + } + } + } + + /** + * Return whether the Details button should be included. This method is + * invoked once when the dialog is built. By default, the Details button is + * only included if the status used when creating the dialog was a + * multi-status or if the status contains an exception. Subclasses may + * override. + * + * @return whether the Details button should be included + * @since 3.1 + */ + protected bool shouldShowDetailsButton() { + return status.isMultiStatus() || status.getException() !is null; + } + + /** + * Set the status displayed by this error dialog to the given status. This + * only affects the status displayed by the Details list. The message, image + * and title should be updated by the subclass, if desired. + * + * @param status + * the status to be displayed in the details list + * @since 3.1 + */ + protected final void setStatus(IStatus status) { + if (this.status !is status) { + this.status = status; + } + shouldIncludeTopLevelErrorInDetails = true; + if (listCreated) { + repopulateList(); + } + } + + /** + * Repopulate the supplied list widget. + */ + private void repopulateList() { + if (list !is null && !list.isDisposed()) { + list.removeAll(); + populateList(list); + } + } + + /* (non-Javadoc) + * @see dwtx.jface.dialogs.IconAndMessageDialog#getColumnCount() + */ + int getColumnCount() { + if (Policy.getErrorSupportProvider() is null) + return 2; + return 3; + } +} diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/dialogs/IconAndMessageDialog.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/dialogs/IconAndMessageDialog.d Mon Mar 31 01:41:52 2008 +0200 @@ -0,0 +1,303 @@ +/******************************************************************************* + * 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 + * Stefan Xenos, IBM - bug 156790: Adopt GridLayoutFactory within JFace + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.dialogs.IconAndMessageDialog; + +import dwtx.jface.dialogs.Dialog; +import dwtx.jface.dialogs.IDialogConstants; + +import dwt.DWT; +import dwt.accessibility.AccessibleAdapter; +import dwt.accessibility.AccessibleEvent; +import dwt.graphics.Image; +import dwt.graphics.Point; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Display; +import dwt.widgets.Label; +import dwt.widgets.Shell; +import dwtx.jface.layout.GridDataFactory; +import dwtx.jface.layout.GridLayoutFactory; +import dwtx.jface.layout.LayoutConstants; +import dwtx.jface.resource.JFaceResources; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * The IconAndMessageDialog is the abstract superclass of dialogs that have an + * icon and a message as the first two widgets. In this dialog the icon and + * message are direct children of the shell in order that they can be read by + * accessibility tools more easily. + */ +public abstract class IconAndMessageDialog : Dialog { + /** + * Message (a localized string). + */ + protected String message; + + /** + * Message label is the label the message is shown on. + */ + protected Label messageLabel; + + /** + * Return the label for the image. + */ + protected Label imageLabel; + + /** + * Constructor for IconAndMessageDialog. + * + * @param parentShell + * the parent shell, or null to create a top-level + * shell + */ + public this(Shell parentShell) { + super(parentShell); + } + + /** + * Create the area the message will be shown in. + *

+ * The parent composite is assumed to use GridLayout as its layout manager, + * since the parent is typically the composite created in + * {@link Dialog#createDialogArea}. + *

+ * @param composite + * The composite to parent from. + * @return Control + */ + protected Control createMessageArea(Composite composite) { + // create composite + // create image + Image image = getImage(); + if (image !is null) { + imageLabel = new Label(composite, DWT.NULL); + image.setBackground(imageLabel.getBackground()); + imageLabel.setImage(image); + addAccessibleListeners(imageLabel, image); + GridDataFactory.fillDefaults().align_(DWT.CENTER, DWT.BEGINNING) + .applyTo(imageLabel); + } + // create message + if (message !is null) { + messageLabel = new Label(composite, getMessageLabelStyle()); + messageLabel.setText(message); + GridDataFactory + .fillDefaults() + .align_(DWT.FILL, DWT.BEGINNING) + .grab(true, false) + .hint( + convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH), + DWT.DEFAULT).applyTo(messageLabel); + } + return composite; + } + + private String getAccessibleMessageFor(Image image) { + if (image.opEquals(getErrorImage())) { + return JFaceResources.getString("error");//$NON-NLS-1$ + } + + if (image.opEquals(getWarningImage())) { + return JFaceResources.getString("warning");//$NON-NLS-1$ + } + + if (image.opEquals(getInfoImage())) { + return JFaceResources.getString("info");//$NON-NLS-1$ + } + + if (image.opEquals(getQuestionImage())) { + return JFaceResources.getString("question"); //$NON-NLS-1$ + } + + return null; + } + + /** + * Add an accessible listener to the label if it can be inferred from the + * image. + * + * @param label + * @param image + */ + private void addAccessibleListeners(Label label, Image image) { + label.getAccessible().addAccessibleListener(new class AccessibleAdapter { + Image image_; + this(){ + image_ = image; + } + public void getName(AccessibleEvent event) { + String accessibleMessage = getAccessibleMessageFor(image_); + if (accessibleMessage is null) { + return; + } + event.result = accessibleMessage; + } + }); + } + + /** + * Returns the style for the message label. + * + * @return the style for the message label + * + * @since 3.0 + */ + protected int getMessageLabelStyle() { + return DWT.WRAP; + } + + /* + * @see Dialog.createButtonBar() + */ + protected Control createButtonBar(Composite parent) { + Composite composite = new Composite(parent, DWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(0) // this is incremented + // by createButton + .equalWidth(true).applyTo(composite); + + GridDataFactory.fillDefaults().align_(DWT.END, DWT.CENTER).span( + 2, 1).applyTo(composite); + composite.setFont(parent.getFont()); + // Add the buttons to the button bar. + createButtonsForButtonBar(composite); + return composite; + } + + /** + * Returns the image to display beside the message in this dialog. + *

+ * Subclasses may override. + *

+ * + * @return the image to display beside the message + * @since 2.0 + */ + protected abstract Image getImage(); + + /* + * @see Dialog.createContents(Composite) + */ + protected Control createContents(Composite parent) { + // initialize the dialog units + initializeDialogUnits(parent); + Point defaultMargins = LayoutConstants.getMargins(); + Point defaultSpacing = LayoutConstants.getSpacing(); + GridLayoutFactory.fillDefaults().margins(defaultMargins.x, + defaultMargins.y * 3 / 2).spacing(defaultSpacing.x * 2, + defaultSpacing.y).numColumns(getColumnCount()).applyTo( + parent); + + GridDataFactory.fillDefaults().grab(true, true).applyTo(parent); + createDialogAndButtonArea(parent); + return parent; + } + + /** + * Get the number of columns in the layout of the Shell of the dialog. + * + * @return int + * @since 3.3 + */ + int getColumnCount() { + return 2; + } + + /** + * Create the dialog area and the button bar for the receiver. + * + * @param parent + */ + protected void createDialogAndButtonArea(Composite parent) { + // create the dialog area and button bar + dialogArea = createDialogArea(parent); + buttonBar = createButtonBar(parent); + // Apply to the parent so that the message gets it too. + applyDialogFont(parent); + } + + /** + * Return the Image to be used when displaying an error. + * + * @return image the error image + */ + public Image getErrorImage() { + return getSWTImage(DWT.ICON_ERROR); + } + + /** + * Return the Image to be used when displaying a warning. + * + * @return image the warning image + */ + public Image getWarningImage() { + return getSWTImage(DWT.ICON_WARNING); + } + + /** + * Return the Image to be used when displaying information. + * + * @return image the information image + */ + public Image getInfoImage() { + return getSWTImage(DWT.ICON_INFORMATION); + } + + /** + * Return the Image to be used when displaying a question. + * + * @return image the question image + */ + public Image getQuestionImage() { + return getSWTImage(DWT.ICON_QUESTION); + } + + /** + * Get an Image from the provide DWT image constant. + * + * @param imageID + * the DWT image constant + * @return image the image + */ + private Image getSWTImage(int imageID) { + Shell shell = getShell(); + Display display; + if (shell is null) { + shell = getParentShell(); + } + if (shell is null) { + display = Display.getCurrent(); + } else { + display = shell.getDisplay(); + } + + Image[1] image; + display.syncExec(new class Runnable { + int imageID_; + Display display_; + this(){ + display_=display; + imageID_=imageID; + } + public void run() { + image[0] = display_.getSystemImage(imageID_); + } + }); + + return image[0]; + + } + +} diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/layout/GridDataFactory.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/layout/GridDataFactory.d Mon Mar 31 01:41:52 2008 +0200 @@ -0,0 +1,486 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Stefan Xenos, IBM - initial implementation, bug 178888 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.layout.GridDataFactory; + +import dwtx.jface.layout.LayoutGenerator; + +import dwt.DWT; +import dwt.graphics.Point; +import dwt.layout.GridData; +import dwt.widgets.Control; + +import dwt.dwthelper.utils; +import tango.core.Exception; + +/** + * This class provides a convienient shorthand for creating and initializing + * GridData. This offers several benefits over creating GridData normal way: + * + *
    + *
  • The same factory can be used many times to create several GridData instances
  • + *
  • The setters on GridDataFactory all return "this", allowing them to be chained
  • + *
  • GridDataFactory uses vector setters (it accepts Points), making it easy to + * set X and Y values together
  • + *
+ * + *

+ * GridDataFactory instances are created using one of the static methods on this class. + *

+ * + *

+ * Example usage: + *

+ *
+ *
+ * ////////////////////////////////////////////////////////////
+ * // Example 1: Typical grid data for a non-wrapping label
+ *
+ *     // GridDataFactory version
+ *     GridDataFactory.fillDefaults().applyTo(myLabel);
+ *
+ *     // Equivalent DWT version
+ *     GridData labelData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
+ *     myLabel.setLayoutData(labelData);
+ *
+ * ///////////////////////////////////////////////////////////
+ * // Example 2: Typical grid data for a wrapping label
+ *
+ *     // GridDataFactory version
+ *     GridDataFactory.fillDefaults()
+ *          .align(DWT.FILL, DWT.CENTER)
+ *          .hint(150, DWT.DEFAULT)
+ *          .grab(true, false)
+ *          .applyTo(wrappingLabel);
+ *
+ *     // Equivalent DWT version
+ *     GridData wrappingLabelData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER);
+ *     wrappingLabelData.minimumWidth = 1;
+ *     wrappingLabelData.widthHint = 150;
+ *     wrappingLabel.setLayoutData(wrappingLabelData);
+ *
+ * //////////////////////////////////////////////////////////////
+ * // Example 3: Typical grid data for a scrollable control (a list box, tree, table, etc.)
+ *
+ *     // GridDataFactory version
+ *     GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox);
+ *
+ *     // Equivalent DWT version
+ *     GridData listBoxData = new GridData(GridData.FILL_BOTH);
+ *     listBoxData.widthHint = 150;
+ *     listBoxData.heightHint = 150;
+ *     listBoxData.minimumWidth = 1;
+ *     listBoxData.minimumHeight = 1;
+ *     listBox.setLayoutData(listBoxData);
+ *
+ * /////////////////////////////////////////////////////////////
+ * // Example 4: Typical grid data for a button
+ *
+ *     // GridDataFactory version
+ *     Point preferredSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, false);
+ *     Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize);
+ *     GridDataFactory.fillDefaults().align(DWT.FILL, DWT.CENTER).hint(hint).applyTo(button);
+ *
+ *     // Equivalent DWT version
+ *     Point preferredSize = button.computeSize(DWT.DEFAULT, DWT.DEFAULT, false);
+ *     Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize);
+ *     GridData buttonData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
+ *     buttonData.widthHint = hint.x;
+ *     buttonData.heightHint = hint.y;
+ *     button.setLayoutData(buttonData);
+ *
+ * /////////////////////////////////////////////////////////////
+ * // Example 5: Generated GridData
+ *
+ *     // Generates GridData a wrapping label that spans 2 columns
+ *     GridDataFactory.generate(wrappingLabel, 2, 1);
+ *
+ *     // Generates GridData for a listbox. and adjusts the preferred size to 300x400 pixels
+ *     GridDataFactory.defaultsFor(listBox).hint(300, 400).applyTo(listBox);
+ *
+ *     // Generates GridData equivalent to example 4
+ *     GridDataFactory.generate(button, 1, 1);
+ *
+ * 
+ * + * @since 3.2 + */ +public final class GridDataFactory { + private GridData data; + + /** + * Creates a GridDataFactory that creates copes of the given GridData. + * + * @param d template GridData to copy + */ + private this(GridData d) { + this.data = d; + } + + /** + * Creates a new GridDataFactory initialized with the DWT defaults. + * This factory will generate GridData that is equivalent to + * "new GridData()". + * + *

+ * Initial values are: + *

+ * + *
    + *
  • align(DWT.BEGINNING, DWT.CENTER)
  • + *
  • exclude(false)
  • + *
  • grab(false, false)
  • + *
  • hint(DWT.DEFAULT, DWT.DEFAULT)
  • + *
  • indent(0,0)
  • + *
  • minSize(0,0)
  • + *
  • span(1,1)
  • + *
+ * + * @return a new GridDataFactory instance + * @see #fillDefaults() + */ + public static GridDataFactory swtDefaults() { + return new GridDataFactory(new GridData()); + } + + /** + * Creates a new GridDataFactory that creates copies of the given GridData + * by default. + * + * @param data GridData to copy + * @return a new GridDataFactory that creates copies of the argument by default + */ + public static GridDataFactory createFrom(GridData data) { + return new GridDataFactory(copyData(data)); + } + + /** + * Creates a GridDataFactory initialized with defaults that will cause + * the control to fill its cell. The minimum size is set to the smallest possible + * minimum size supported by DWT. Currently, the smallest supported minimum size + * is (1,1) so this is the default. If GridLayout ever adds support for grid data + * with no minimum size, this will be changed to 0,0 in the future. + * + *

+ * Initial values are: + *

+ * + *
    + *
  • align(DWT.FILL, DWT.FILL)
  • + *
  • exclude(false)
  • + *
  • grab(false, false)
  • + *
  • hint(DWT.DEFAULT, DWT.DEFAULT)
  • + *
  • indent(0,0)
  • + *
  • minSize(1,1)
  • + *
  • span(1,1)
  • + *
+ * + * @return a GridDataFactory that makes controls fill their grid by default + * + * @see #swtDefaults() + */ + public static GridDataFactory fillDefaults() { + GridData data = new GridData(); + data.minimumWidth = 1; + data.minimumHeight = 1; + data.horizontalAlignment = DWT.FILL; + data.verticalAlignment = DWT.FILL; + + return new GridDataFactory(data); + } + + /** + * Returns a GridDataFactory initialized with heuristicly generated defaults for the given control. + * To be precise, this method picks the default values that GridLayoutFactory.generateLayout + * would have assigned to the control. Does not attach GridData to the control. Callers must + * additionally call applyTo(theControl) if they wish to use the generated values. + * + *

+ * This method is intended for situations where generateLayout is generating layout data + * for a particular control that is not quite right for the desired layout. + * This allows callers to start with the generated values and tweak one or two settings + * before applying the GridData to the control. + *

+ * + * @see GridLayoutFactory#generateLayout(dwt.widgets.Composite) + * @param theControl + * @return a GridLayoutFactory initialized with defaults that GridLayoutFactory would have + * @since 3.3 + */ + public static GridDataFactory defaultsFor(Control theControl) { + return LayoutGenerator.defaultsFor(theControl); + } + + /** + * Generates layout data to the given control, given the number of cells + * spanned by the control. Attaches a GridData to the control. This method + * allows generated layout data to be used with controls that span multiple cells. + *

+ * The generated layout data is the same as what would be generated by + * GridLayoutFactory.generateLayout, except that the span is configurable + *

+ * + * @see GridLayoutFactory#generateLayout(dwt.widgets.Composite) + * @param theControl + * @param hSpan number of columns spanned by the control + * @param vSpan number of rows spanned by the control + * @since 3.3 + */ + public static void generate(Control theControl, int hSpan, int vSpan) { + defaultsFor(theControl).span(hSpan, vSpan).applyTo(theControl); + } + + /** + * Generates layout data to the given control, given the number of cells + * spanned by the control. Attaches GridData to the control. This method + * allows generated layout data to be used with controls that span multiple cells. + *

+ * The generated layout data is the same as what would be generated by + * GridLayoutFactory.generateLayout, except that the span is configurable + *

+ * + * @see GridLayoutFactory#generateLayout(dwt.widgets.Composite) + * @param theControl + * @param span The x coordinate indicates the number of + * columns spanned, and the y coordinate indicates the number of rows. + * @since 3.3 + */ + public static void generate(Control theControl, Point span) { + defaultsFor(theControl).span(span).applyTo(theControl); + } + + /** + * Sets the GridData span. The span controls how many cells + * are filled by the control. + * + * @param hSpan number of columns spanned by the control + * @param vSpan number of rows spanned by the control + * @return this + */ + public GridDataFactory span(int hSpan, int vSpan) { + data.horizontalSpan = hSpan; + data.verticalSpan = vSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells + * are filled by the control. + * + * @param span the new span. The x coordinate indicates the number of + * columns spanned, and the y coordinate indicates the number of rows. + * @return this + */ + public GridDataFactory span(Point span) { + data.horizontalSpan = span.x; + data.verticalSpan = span.y; + return this; + } + + /** + * Sets the width and height hints. The width and height hints override + * the control's preferred size. If either hint is set to DWT.DEFAULT, + * the control's preferred size is used. + * + * @param xHint horizontal hint (pixels), or DWT.DEFAULT to use the control's preferred size + * @param yHint vertical hint (pixels), or DWT.DEFAULT to use the control's preferred size + * @return this + */ + public GridDataFactory hint(int xHint, int yHint) { + data.widthHint = xHint; + data.heightHint = yHint; + return this; + } + + /** + * Sets the width and height hints. The width and height hints override + * the control's preferred size. If either hint is set to DWT.DEFAULT, + * the control's preferred size is used. + * + * @param hint size (pixels) to be used instead of the control's preferred size. If + * the x or y values are set to DWT.DEFAULT, the control's computeSize() method will + * be used to obtain that dimension of the preferred size. + * @return this + */ + public GridDataFactory hint(Point hint) { + data.widthHint = hint.x; + data.heightHint = hint.y; + return this; + } + + /** + * Sets the alignment of the control within its cell. + * + * @param hAlign horizontal alignment. One of DWT.BEGINNING, DWT.CENTER, DWT.END, or DWT.FILL. + * @param vAlign vertical alignment. One of DWT.BEGINNING, DWT.CENTER, DWT.END, or DWT.FILL. + * @return this + */ + public GridDataFactory align_(int hAlign, int vAlign) { + if (hAlign !is DWT.BEGINNING && hAlign !is DWT.CENTER && hAlign !is GridData.CENTER && hAlign !is DWT.END && hAlign !is GridData.END && hAlign !is DWT.FILL && hAlign !is DWT.LEFT && hAlign !is DWT.RIGHT) { + throw new IllegalArgumentException(null); + } + if (vAlign !is DWT.BEGINNING && vAlign !is DWT.CENTER && vAlign !is GridData.CENTER && vAlign !is DWT.END && vAlign !is GridData.END && vAlign !is DWT.FILL && vAlign !is DWT.TOP && vAlign !is DWT.BOTTOM) { + throw new IllegalArgumentException(null); + } + data.horizontalAlignment = hAlign; + data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the indent of the control within the cell. Moves the position of the control + * by the given number of pixels. Positive values move toward the lower-right, negative + * values move toward the upper-left. + * + * @param hIndent distance to move to the right (negative values move left) + * @param vIndent distance to move down (negative values move up) + * @return this + */ + public GridDataFactory indent(int hIndent, int vIndent) { + data.horizontalIndent = hIndent; + data.verticalIndent = vIndent; + return this; + } + + /** + * Sets the indent of the control within the cell. Moves the position of the control + * by the given number of pixels. Positive values move toward the lower-right, negative + * values move toward the upper-left. + * + * @param indent offset to move the control + * @return this + */ + public GridDataFactory indent(Point indent) { + data.horizontalIndent = indent.x; + data.verticalIndent = indent.y; + return this; + } + + /** + * Determines whether extra horizontal or vertical space should be allocated to + * this control's column when the layout resizes. If any control in the column + * is set to grab horizontal then the whole column will grab horizontal space. + * If any control in the row is set to grab vertical then the whole row will grab + * vertical space. + * + * @param horizontal true if the control's column should grow horizontally + * @param vertical true if the control's row should grow vertically + * @return this + */ + public GridDataFactory grab(bool horizontal, bool vertical) { + data.grabExcessHorizontalSpace = horizontal; + data.grabExcessVerticalSpace = vertical; + return this; + } + + /** + * Sets the minimum size for the control. The control will not be permitted + * to shrink below this size. Note: GridLayout treats a minimum size of 0 + * as an undocumented special value, so the smallest possible minimum size + * is a size of 1. A minimum size of DWT.DEFAULT indicates that the result + * of computeSize(int, int, bool) should be used as the control's minimum + * size. + * + * + * @param minX minimum a value of 1 or more is a horizontal size of the control (pixels). + * DWT.DEFAULT indicates that the control's preferred size should be used. A size + * of 0 has special semantics defined by GridLayout. + * @param minY minimum a value of 1 or more is a vertical size of the control (pixels). DWT.DEFAULT + * indicates that the control's preferred size should be used. A size + * of 0 has special semantics defined by GridLayout. + * @return this + */ + public GridDataFactory minSize(int minX, int minY) { + data.minimumWidth = minX; + data.minimumHeight = minY; + return this; + } + + /** + * Sets the minimum size for the control. The control will not be permitted + * to shrink below this size. Note: GridLayout treats a minimum size of 0 + * as an undocumented special value, so the smallest possible minimum size + * is a size of 1. A minimum size of DWT.DEFAULT indicates that the result + * of computeSize(int, int, bool) should be used as the control's minimum + * size. + * + * @param min minimum size of the control + * @return this + */ + public GridDataFactory minSize(Point min) { + data.minimumWidth = min.x; + data.minimumHeight = min.y; + return this; + } + + /** + * Instructs the GridLayout to ignore this control when performing layouts. + * + * @param shouldExclude true iff the control should be excluded from layouts + * @return this + */ + public GridDataFactory exclude(bool shouldExclude) { + data.exclude = shouldExclude; + return this; + } + + /** + * Creates a new GridData instance. All attributes of the GridData instance + * will be initialized by the factory. + * + * @return a new GridData instance + */ + public GridData create() { + return copyData(data); + } + + /** + * Creates a copy of the reciever. + * + * @return a copy of the reciever + */ + public GridDataFactory copy() { + return new GridDataFactory(create()); + } + + /** + * Returns a copy of the given GridData + * + * @param data GridData to copy + * @return a copy of the argument + */ + public static GridData copyData(GridData data) { + GridData newData = new GridData(data.horizontalAlignment, data.verticalAlignment, data.grabExcessHorizontalSpace, data.grabExcessVerticalSpace, data.horizontalSpan, + data.verticalSpan); + newData.exclude = data.exclude; + newData.heightHint = data.heightHint; + newData.horizontalIndent = data.horizontalIndent; + newData.minimumHeight = data.minimumHeight; + newData.minimumWidth = data.minimumWidth; + newData.verticalIndent = data.verticalIndent; + newData.widthHint = data.widthHint; + + return newData; + } + + /** + * Sets the layout data on the given control. Creates a new GridData instance and + * assigns it to the control by calling control.setLayoutData. + * + * @param control control whose layout data will be initialized + */ + public void applyTo(Control control) { + control.setLayoutData(create()); + } + +} diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/layout/GridLayoutFactory.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/layout/GridLayoutFactory.d Mon Mar 31 01:41:52 2008 +0200 @@ -0,0 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Stefan Xenos, IBM - initial implementation, bug 178888 + * Karsten Stoeckmann - bug 156982 + * Port to the D programming language: + * Frank Benoit + *******************************************************************************/ +module dwtx.jface.layout.GridLayoutFactory; + +import dwtx.jface.layout.LayoutConstants; +import dwtx.jface.layout.LayoutGenerator; + +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.layout.GridLayout; +import dwt.widgets.Composite; + +import dwt.dwthelper.utils; + +/** + * GridLayoutFactory creates and initializes grid layouts. There are two ways to use GridLayoutFactory. + * Normally, it is used as a shorthand for writing "new GridLayout()" and initializing a bunch + * of fields. In this case the main benefit is a more concise syntax and the ability to create more + * than one identical GridLayout from the same factory. Changing a property of the factory will affect + * future layouts created by the factory, but has no effect on layouts that have already been created. + * + *

+ * GridLayoutFactory can also generate grid data for all the controls in a layout. This is done with + * the generateLayout method. To use this feature: + *

+ * + *
    + *
  1. Create the composite
  2. + *
  3. Create all the controls in the composite
  4. + *
  5. Call generateLayout
  6. + *
+ * + *

+ * The order here is important. generateLayout must be called after all the child controls have + * been created. generateLayout will not change any layout data that has already been attached + * to a child control and it will not recurse into nested composites. + *

+ * + * @since 3.2 + */ +public final class GridLayoutFactory { + + /** + * Template layout. The factory will create copies of this layout. + */ + private GridLayout l; + + /** + * Creates a new GridLayoutFactory that will create copies of the given layout. + * + * @param l layout to copy + */ + private this(GridLayout l) { + this.l = l; + } + + /** + * Creates a factory that creates copies of the given layout. + * + * @param l layout to copy + * @return a new GridLayoutFactory instance that creates copies of the given layout + */ + public static GridLayoutFactory createFrom(GridLayout l) { + return new GridLayoutFactory(copyLayout(l)); + } + + /** + * Creates a copy of the reciever. + * + * @return a copy of the reciever + */ + public GridLayoutFactory copy() { + return new GridLayoutFactory(create()); + } + + /** + * Creates a GridLayoutFactory that creates GridLayouts with the default DWT + * values. + * + *

+ * Initial values are: + *

+ * + *
    + *
  • numColumns(1)
  • + *
  • margins(5,5)
  • + *
  • extendedMargins(0,0,0,0)
  • + *
  • spacing(5,5)
  • + *
  • equalWidth(false)
  • + *
+ * + * @return a GridLayoutFactory that creates GridLayouts as though created with + * their default constructor + * @see #fillDefaults + */ + public static GridLayoutFactory swtDefaults() { + return new GridLayoutFactory(new GridLayout()); + } + + /** + * Creates a GridLayoutFactory that creates GridLayouts with no margins and + * default dialog spacing. + * + *

+ * Initial values are: + *

+ * + *
    + *
  • numColumns(1)
  • + *
  • margins(0,0)
  • + *
  • extendedMargins(0,0,0,0)
  • + *
  • spacing(LayoutConstants.getSpacing())
  • + *
  • equalWidth(false)
  • + *
+ * + * @return a GridLayoutFactory that creates GridLayouts as though created with + * their default constructor + * @see #swtDefaults + */ + public static GridLayoutFactory fillDefaults() { + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + Point defaultSpacing = LayoutConstants.getSpacing(); + layout.horizontalSpacing = defaultSpacing.x; + layout.verticalSpacing = defaultSpacing.y; + return new GridLayoutFactory(layout); + } + + /** + * Sets whether the columns should be forced to be equal width + * + * @param equal true iff the columns should be forced to be equal width + * @return this + */ + public GridLayoutFactory equalWidth(bool equal) { + l.makeColumnsEqualWidth = equal; + return this; + } + + /** + * Sets the spacing for layouts created with this factory. The spacing + * is the distance between cells within the layout. + * + * @param hSpacing horizontal spacing (pixels) + * @param vSpacing vertical spacing (pixels) + * @return this + * @see #margins(Point) + * @see #margins(int, int) + */ + public GridLayoutFactory spacing(int hSpacing, int vSpacing) { + l.horizontalSpacing = hSpacing; + l.verticalSpacing = vSpacing; + return this; + } + + /** + * Sets the spacing for layouts created with this factory. The spacing + * is the distance between cells within the layout. + * + * @param spacing space between controls in the layout (pixels) + * @return this + * @see #margins(Point) + * @see #margins(int, int) + */ + public GridLayoutFactory spacing(Point spacing) { + l.horizontalSpacing = spacing.x; + l.verticalSpacing = spacing.y; + return this; + } + + /** + * Sets the margins for layouts created with this factory. The margins + * are the distance between the outer cells and the edge of the layout. + * + * @param margins margin size (pixels) + * @return this + * @see #spacing(Point) + * @see #spacing(int, int) + */ + public GridLayoutFactory margins(Point margins) { + l.marginWidth = margins.x; + l.marginHeight = margins.y; + return this; + } + + /** + * Sets the margins for layouts created with this factory. The margins + * specify the number of pixels of horizontal and vertical margin that will + * be placed along the left/right and top/bottom edges of the layout. Note + * that thes margins will be added to the ones specified by + * {@link #extendedMargins(int, int, int, int)}. + * + * @param width + * margin width (pixels) + * @param height + * margin height (pixels) + * @return this + * @see #spacing(Point) + * * @see #spacing(int, int) + */ + public GridLayoutFactory margins(int width, int height) { + l.marginWidth = width; + l.marginHeight = height; + return this; + } + + /** + * Sets the margins for layouts created with this factory. The margins + * specify the number of pixels of horizontal and vertical margin that will + * be placed along the left, right, top, and bottom edges of the layout. + * Note that thes margins will be added to the ones specified by + * {@link #margins(int, int)}. + * + * @param left + * left margin size (pixels) + * @param right + * right margin size (pixels) + * @param top + * top margin size (pixels) + * @param bottom + * bottom margin size (pixels) + * @return this + * @see #spacing(Point) + * @see #spacing(int, int) + * + * @since 3.3 + */ + public GridLayoutFactory extendedMargins(int left, int right, int top, int bottom) { + l.marginLeft = left; + l.marginRight = right; + l.marginTop = top; + l.marginBottom = bottom; + return this; + } + + /** + * Sets the margins for layouts created with this factory. The margins + * specify the number of pixels of horizontal and vertical margin that will + * be placed along the left, right, top, and bottom edges of the layout. + * Note that thes margins will be added to the ones specified by + * {@link #margins(int, int)}. + * + *
+     *     // Construct a GridLayout whose left, right, top, and bottom
+     *     // margin sizes are 10, 5, 0, and 15 respectively
+     *
+     *     Rectangle margins = Geometry.createDiffRectangle(10,5,0,15);
+     *     GridLayoutFactory.fillDefaults().extendedMargins(margins).applyTo(composite1);
+     * 
+ * + * @param differenceRect rectangle which, when added to the client area of the + * layout, returns the outer area of the layout. The x and y values of + * the rectangle correspond to the position of the bounds of the + * layout with respect to the client area. They should be negative. + * The width and height correspond to the relative size of the bounds + * of the layout with respect to the client area, and should be positive. + * @return this + * @see #spacing(Point) + * @see #spacing(int, int) + * + * @since 3.3 + */ + public GridLayoutFactory extendedMargins(Rectangle differenceRect) { + l.marginLeft = -differenceRect.x; + l.marginTop = -differenceRect.y; + l.marginBottom = differenceRect.y + differenceRect.height; + l.marginRight = differenceRect.x + differenceRect.width; + return this; + } + + /** + * Sets the number of columns in the layout + * + * @param numColumns number of columns in the layout + * @return this + */ + public GridLayoutFactory numColumns(int numColumns) { + l.numColumns = numColumns; + return this; + } + + /** + * Creates a new GridLayout, and initializes it with values from the factory. + * + * @return a new initialized GridLayout. + * @see #applyTo + */ + public GridLayout create() { + return copyLayout(l); + } + + /** + * Creates a new GridLayout and attaches it to the given composite. + * Does not create the GridData of any of the controls in the composite. + * + * @param c composite whose layout will be set + * @see #generateLayout + * @see #create + * @see GridLayoutFactory + */ + public void applyTo(Composite c) { + c.setLayout(copyLayout(l)); + } + + /** + * Copies the given GridLayout instance + * + * @param l layout to copy + * @return a new GridLayout + */ + public static GridLayout copyLayout(GridLayout l) { + GridLayout result = new GridLayout(l.numColumns, l.makeColumnsEqualWidth); + result.horizontalSpacing = l.horizontalSpacing; + result.marginBottom = l.marginBottom; + result.marginHeight = l.marginHeight; + result.marginLeft = l.marginLeft; + result.marginRight = l.marginRight; + result.marginTop = l.marginTop; + result.marginWidth = l.marginWidth; + result.verticalSpacing = l.verticalSpacing; + + return result; + } + + /** + * Applies this layout to the given composite, and attaches default GridData + * to all immediate children that don't have one. The layout is generated using + * heuristics based on the widget types. In most cases, it will create exactly the same + * layout that would have been hardcoded by the programmer. In any situation + * where it does not produce the desired layout, the GridData for any child can be + * overridden by attaching the layout data before calling this method. In these cases, + * the special-case layout data can be hardcoded and the algorithm can supply defaults + * to the rest. + * + *

+ * This must be called AFTER all of the child controls have been created and their + * layouts attached. This method will attach a layout to the given composite. If any new + * children are created after calling this method, their GridData must be created manually. + * The algorithm does not recurse into child composites. To generate all the layouts in + * a widget hierarchy, the method must be called bottom-up for each Composite. + *

+ * + *

+ * All controls are made to span a single cell. The algorithm tries to classify controls into one + * of the following categories: + *

+ * + *
    + *
  • Pushbuttons: Set to a constant size large enough to fit their text and no smaller + * than the default button size.
  • + *
  • Wrapping with text (labels, read-only text boxes, etc.): override the preferred horizontal + * size with the default wrapping point, fill horizontally, grab horizontal space, keep the + * preferred vertical size
  • + *
  • Wrapping without text (toolbars, coolbars, etc.): fill align, don't grab, use the preferred size
  • + *
  • Horizontally scrolling controls (anything with horizontal scrollbars or where the user edits + * text and can cursor through it from left-to-right): override the preferred horizontal size with + * a constant, grab horizontal, fill horizontal.
  • + *
  • Vertically scrolling controls (anything with vertical scrollbars or where the user edits + * text and can cursor through it up and down): override the preferred vertical size with a constant, + * grab vertical, fill vertical
  • + *
  • Nested layouts: fill align both directions, grab along any dimension if the layout would + * be able to expand along that dimension.
  • + *
  • Non-wrapping non-scrollable read-only text: fill horizontally, center vertically, default size, don't grab
  • + *
  • Non-wrapping non-scrollable non-text: fill both, default size, don't grab
  • + *
+ * + * @param c composite whose layout will be generated + */ + public void generateLayout(Composite c) { + applyTo(c); + LayoutGenerator.generateLayout(c); + } +} diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/layout/LayoutConstants.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/layout/LayoutConstants.d Mon Mar 31 01:41:52 2008 +0200 @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2005, 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.layout.LayoutConstants; + + +import dwt.graphics.FontMetrics; +import dwt.graphics.GC; +import dwt.graphics.Point; +import dwt.widgets.Display; +import dwtx.jface.dialogs.Dialog; +import dwtx.jface.dialogs.IDialogConstants; +import dwtx.jface.resource.JFaceResources; + +/** + * Contains various layout constants + * + * @since 3.2 + */ +public final class LayoutConstants { + private static Point dialogMargins = null; + private static Point dialogSpacing = null; + private static Point minButtonSize = null; + + private static void initializeConstants() { + if (dialogMargins !is null) { + return; + } + + GC gc = new GC(Display.getCurrent()); + gc.setFont(JFaceResources.getDialogFont()); + FontMetrics fontMetrics = gc.getFontMetrics(); + + dialogMargins = new Point(Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.HORIZONTAL_MARGIN), + Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_MARGIN)); + + dialogSpacing = new Point(Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.HORIZONTAL_SPACING), + Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_SPACING)); + + minButtonSize = new Point(Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH), 0); + + gc.dispose(); + } + + /** + * Returns the default dialog margins, in pixels + * + * @return the default dialog margins, in pixels + */ + public static final Point getMargins() { + initializeConstants(); + return dialogMargins; + } + + /** + * Returns the default dialog spacing, in pixels + * + * @return the default dialog spacing, in pixels + */ + public static final Point getSpacing() { + initializeConstants(); + return dialogSpacing; + } + + /** + * Returns the default minimum button size, in pixels + * + * @return the default minimum button size, in pixels + */ + public static final Point getMinButtonSize() { + initializeConstants(); + return minButtonSize; + } +} diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/layout/LayoutGenerator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/layout/LayoutGenerator.d Mon Mar 31 01:41:52 2008 +0200 @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2005, 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.layout.LayoutGenerator; + +import dwtx.jface.layout.GridDataFactory; +import dwtx.jface.layout.LayoutConstants; + +import dwt.DWT; +import dwt.events.ModifyListener; +import dwt.graphics.Point; +import dwt.layout.GridData; +import dwt.layout.GridLayout; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Layout; +import dwt.widgets.Scrollable; +import dwtx.jface.util.Geometry; + +import dwt.dwthelper.utils; + +/* package */class LayoutGenerator { + + /** + * Default size for controls with varying contents + */ + private static const Point defaultSize; + + /** + * Default wrapping size for wrapped labels + */ + private static const int wrapSize = 350; + + private static const GridDataFactory nonWrappingLabelData; + + static this(){ + defaultSize = new Point(150, 150); + nonWrappingLabelData = GridDataFactory.fillDefaults().align_(DWT.BEGINNING, DWT.CENTER).grab(false, false); + } + + private static bool hasStyle(Control c, int style) { + return (c.getStyle() & style) !is 0; + } + + /** + * Generates a GridLayout for the given composite by examining its child + * controls and attaching layout data to any immediate children that do not + * already have layout data. + * + * @param toGenerate + * composite to generate a layout for + */ + public static void generateLayout(Composite toGenerate) { + Control[] children = toGenerate.getChildren(); + + for (int i = 0; i < children.length; i++) { + Control control = children[i]; + + // Skip any children that already have layout data + if (control.getLayoutData() !is null) { + continue; + } + + applyLayoutDataTo(control); + } + } + + private static void applyLayoutDataTo(Control control) { + defaultsFor(control).applyTo(control); + } + + /** + * Creates default factory for this control types: + *
    + *
  • {@link Button} with {@link DWT#CHECK}
  • + *
  • {@link Button}
  • + *
  • {@link Composite}
  • + *
+ * @param control the control the factory is search for + * @return a default factory for the control + */ + public static GridDataFactory defaultsFor(Control control) { + if ( auto button = cast(Button) control ) { + + if (hasStyle(button, DWT.CHECK)) { + return nonWrappingLabelData.copy(); + } else { + return GridDataFactory.fillDefaults().align_(DWT.FILL, DWT.CENTER).hint(Geometry.max(button.computeSize(DWT.DEFAULT, DWT.DEFAULT, true), LayoutConstants.getMinButtonSize())); + } + } + + if (auto scrollable = cast(Scrollable) control ) { + + if ( auto composite = cast(Composite) scrollable ) { + + Layout theLayout = composite.getLayout(); + if ( cast(GridLayout) theLayout ) { + bool growsHorizontally = false; + bool growsVertically = false; + + Control[] children = composite.getChildren(); + for (int i = 0; i < children.length; i++) { + Control child = children[i]; + + GridData data = cast(GridData) child.getLayoutData(); + + if (data !is null) { + if (data.grabExcessHorizontalSpace) { + growsHorizontally = true; + } + if (data.grabExcessVerticalSpace) { + growsVertically = true; + } + } + } + + return GridDataFactory.fillDefaults().grab(growsHorizontally, growsVertically); + } + } + } + + bool wrapping = hasStyle(control, DWT.WRAP); + + // Assume any control with the H_SCROLL or V_SCROLL flags are + // horizontally or vertically + // scrollable, respectively. + bool hScroll = hasStyle(control, DWT.H_SCROLL); + bool vScroll = hasStyle(control, DWT.V_SCROLL); + + bool containsText = hasMethod(control, "setText", [ ArrayWrapperString.classinfo ] ); //$NON-NLS-1$ + + // If the control has a setText method, an addModifyListener method, and + // does not have + // the DWT.READ_ONLY flag, assume it contains user-editable text. + bool userEditable = !hasStyle(control, DWT.READ_ONLY) && containsText && hasMethod(control, "addModifyListener", [ ModifyListener.classinfo ]); //$NON-NLS-1$ + + // For controls containing user-editable text... + if (userEditable) { + if (hasStyle(control, DWT.MULTI)) { + vScroll = true; + } + + if (!wrapping) { + hScroll = true; + } + } + + // Compute the horizontal hint + int hHint = DWT.DEFAULT; + bool grabHorizontal = hScroll; + + // For horizontally-scrollable controls, override their horizontal + // preferred size + // with a constant + if (hScroll) { + hHint = defaultSize.x; + } else { + // For wrapping controls, there are two cases. + // 1. For controls that contain text (like wrapping labels, + // read-only text boxes, + // etc.) override their preferred size with the preferred wrapping + // point and + // make them grab horizontal space. + // 2. For non-text controls (like wrapping toolbars), assume that + // their non-wrapped + // size is best. + + if (wrapping) { + if (containsText) { + hHint = wrapSize; + grabHorizontal = true; + } + } + } + + int vAlign = DWT.FILL; + + // Heuristic for labels: Controls that contain non-wrapping read-only + // text should be + // center-aligned rather than fill-aligned + if (!vScroll && !wrapping && !userEditable && containsText) { + vAlign = DWT.CENTER; + } + + return GridDataFactory.fillDefaults().grab(grabHorizontal, vScroll).align_(DWT.FILL, vAlign).hint(hHint, vScroll ? defaultSize.y : DWT.DEFAULT); + } + + private static bool hasMethod(Control control, String name, ClassInfo[] parameterTypes) { + ClassInfo c = control.classinfo; + implMissing(__FILE__,__LINE__); + pragma(msg, "FIXME dwtx.jface.layout.LayoutGenerator hasMethod reflection" ); + return true; +/+ try { + return c.getMethod(name, parameterTypes) !is null; + } catch (SecurityException e) { + return false; + } catch (NoSuchMethodException e) { + return false; + }+/ + } +} diff -r 8ec40848221b -r 6886832e1ed8 dwtx/jface/resource/JFaceResources.d --- a/dwtx/jface/resource/JFaceResources.d Mon Mar 31 01:07:26 2008 +0200 +++ b/dwtx/jface/resource/JFaceResources.d Mon Mar 31 01:41:52 2008 +0200 @@ -149,8 +149,9 @@ * the message arguments * @return the string */ - public static String format(String key, Object[] args) { - return Format(getString(key), args); + public static String format(String key, ... ) { + implMissing(__FILE__,__LINE__); + return key;//Format(getString(key), args); } /**