Mercurial > projects > dwt-addons
diff dwtx/jface/preference/PreferenceDialog.d @ 9:6c14e54dfc11
completed /jface/resource/
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 29 Mar 2008 02:25:12 +0100 |
parents | |
children | b3c8e32d406f |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/preference/PreferenceDialog.d Sat Mar 29 02:25:12 2008 +0100 @@ -0,0 +1,1518 @@ +/******************************************************************************* + * 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 + * Teddy Walker <teddy.walker@googlemail.com> + * - Bug 188056 [Preferences] PreferencePages have to less indent in PreferenceDialog + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.preference.PreferenceDialog; + +import dwt.dwthelper.utils; + +pragma( msg, "FIXME dwtx.jface.preference.PreferenceDialog" ); +class PreferenceDialog{ + public static const String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$ +} + +/++ +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import dwt.DWT; +import dwt.custom.BusyIndicator; +import dwt.custom.ScrolledComposite; +import dwt.events.ControlAdapter; +import dwt.events.ControlEvent; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.events.HelpEvent; +import dwt.events.HelpListener; +import dwt.events.SelectionAdapter; +import dwt.events.SelectionEvent; +import dwt.events.ShellAdapter; +import dwt.events.ShellEvent; +import dwt.graphics.Font; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.layout.FormAttachment; +import dwt.layout.FormData; +import dwt.layout.FormLayout; +import dwt.layout.GridData; +import dwt.layout.GridLayout; +import dwt.widgets.Button; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Event; +import dwt.widgets.Label; +import dwt.widgets.Layout; +import dwt.widgets.Listener; +import dwt.widgets.Sash; +import dwt.widgets.Shell; +import dwt.widgets.Tree; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.ISafeRunnable; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.ListenerList; +import dwtx.core.runtime.SafeRunner; +import dwtx.core.runtime.Status; +import dwtx.jface.dialogs.DialogMessageArea; +import dwtx.jface.dialogs.IDialogConstants; +import dwtx.jface.dialogs.IMessageProvider; +import dwtx.jface.dialogs.IPageChangeProvider; +import dwtx.jface.dialogs.IPageChangedListener; +import dwtx.jface.dialogs.MessageDialog; +import dwtx.jface.dialogs.PageChangedEvent; +import dwtx.jface.dialogs.TrayDialog; +import dwtx.jface.resource.JFaceResources; +import dwtx.jface.util.IPropertyChangeListener; +import dwtx.jface.util.Policy; +import dwtx.jface.util.PropertyChangeEvent; +import dwtx.jface.util.SafeRunnable; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.ISelectionChangedListener; +import dwtx.jface.viewers.IStructuredSelection; +import dwtx.jface.viewers.SelectionChangedEvent; +import dwtx.jface.viewers.StructuredSelection; +import dwtx.jface.viewers.TreeViewer; +import dwtx.jface.viewers.ViewerFilter; + +/** + * A preference dialog is a hierarchical presentation of preference pages. Each + * page is represented by a node in the tree shown on the left hand side of the + * dialog; when a node is selected, the corresponding page is shown on the right + * hand side. + */ +public class PreferenceDialog extends TrayDialog implements IPreferencePageContainer, IPageChangeProvider { + /** + * Layout for the page container. + * + */ + private class PageLayout extends Layout { + public Point computeSize(Composite composite, int wHint, int hHint, bool force) { + if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) { + return new Point(wHint, hHint); + } + int x = minimumPageSize.x; + int y = minimumPageSize.y; + Control[] children = composite.getChildren(); + for (int i = 0; i < children.length; i++) { + Point size = children[i].computeSize(DWT.DEFAULT, DWT.DEFAULT, force); + x = Math.max(x, size.x); + y = Math.max(y, size.y); + } + + //As pages can implement thier own computeSize + //take it into account + if(currentPage !is null){ + Point size = currentPage.computeSize(); + x = Math.max(x, size.x); + y = Math.max(y, size.y); + } + + if (wHint !is DWT.DEFAULT) { + x = wHint; + } + if (hHint !is DWT.DEFAULT) { + y = hHint; + } + return new Point(x, y); + } + + public void layout(Composite composite, bool force) { + Rectangle rect = composite.getClientArea(); + Control[] children = composite.getChildren(); + for (int i = 0; i < children.length; i++) { + children[i].setSize(rect.width, rect.height); + } + } + } + + //The id of the last page that was selected + private static String lastPreferenceId = null; + + //The last known tree width + private static int lastTreeWidth = 180; + + /** + * Indentifier for the error image + */ + public static final String PREF_DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR; + + /** + * Title area fields + */ + public static final String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$ + + /** + * Return code used when dialog failed + */ + protected static final int FAILED = 2; + + /** + * The current preference page, or <code>null</code> if there is none. + */ + private IPreferencePage currentPage; + + private DialogMessageArea messageArea; + + private Point lastShellSize; + + private IPreferenceNode lastSuccessfulNode; + + /** + * The minimum page size; 400 by 400 by default. + * + * @see #setMinimumPageSize(Point) + */ + private Point minimumPageSize = new Point(400, 400); + + /** + * The OK button. + */ + private Button okButton; + + /** + * The Composite in which a page is shown. + */ + private Composite pageContainer; + + /** + * The preference manager. + */ + private PreferenceManager preferenceManager; + + /** + * Flag for the presence of the error message. + */ + private bool showingError = false; + + /** + * Preference store, initially <code>null</code> meaning none. + * + * @see #setPreferenceStore + */ + private IPreferenceStore preferenceStore; + + private Composite titleArea; + + /** + * The tree viewer. + */ + private TreeViewer treeViewer; + + private ListenerList pageChangedListeners = new ListenerList(); + + /** + * Composite with a FormLayout to contain the title area + */ + Composite formTitleComposite; + + private ScrolledComposite scrolled; + + /** + * Creates a new preference dialog under the control of the given preference + * manager. + * + * @param parentShell + * the parent shell + * @param manager + * the preference manager + */ + public PreferenceDialog(Shell parentShell, PreferenceManager manager) { + super(parentShell); + setShellStyle(getShellStyle() | DWT.RESIZE | DWT.MAX); + preferenceManager = manager; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.Dialog#buttonPressed(int) + */ + protected void buttonPressed(int buttonId) { + switch (buttonId) { + case IDialogConstants.OK_ID: { + okPressed(); + return; + } + case IDialogConstants.CANCEL_ID: { + cancelPressed(); + return; + } + case IDialogConstants.HELP_ID: { + helpPressed(); + return; + } + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.Dialog#cancelPressed() + */ + protected void cancelPressed() { + // Inform all pages that we are cancelling + Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator(); + while (nodes.hasNext()) { + final IPreferenceNode node = (IPreferenceNode) nodes.next(); + if (getPage(node) !is null) { + SafeRunnable.run(new SafeRunnable() { + public void run() { + if (!getPage(node).performCancel()) { + return; + } + } + }); + } + } + setReturnCode(CANCEL); + close(); + } + + /** + * Clear the last selected node. This is so that we not chache the last + * selection in case of an error. + */ + void clearSelectedNode() { + setSelectedNodePreference(null); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#close() + */ + public bool close() { + + //Do this is in a SafeRunnable as it may run client code + SafeRunnable runnable = new SafeRunnable(){ + /* (non-Javadoc) + * @see dwtx.core.runtime.ISafeRunnable#run() + */ + public void run() throws Exception { + List nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER); + for (int i = 0; i < nodes.size(); i++) { + IPreferenceNode node = (IPreferenceNode) nodes.get(i); + node.disposeResources(); + } + + } + + /* (non-Javadoc) + * @see dwtx.jface.util.SafeRunnable#handleException(java.lang.Throwable) + */ + public void handleException(Throwable e) { + super.handleException(e); + clearSelectedNode();//Do not cache a node with problems + } + }; + + SafeRunner.run(runnable); + + return super.close(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#configureShell(dwt.widgets.Shell) + */ + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(JFaceResources.getString("PreferenceDialog.title")); //$NON-NLS-1$ + newShell.addShellListener(new ShellAdapter() { + public void shellActivated(ShellEvent e) { + if (lastShellSize is null) { + lastShellSize = getShell().getSize(); + } + } + + }); + + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#constrainShellSize() + */ + protected void constrainShellSize() { + super.constrainShellSize(); + // record opening shell size + if (lastShellSize is null) { + lastShellSize = getShell().getSize(); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.Dialog#createButtonsForButtonBar(dwt.widgets.Composite) + */ + protected void createButtonsForButtonBar(Composite parent) { + // create OK and Cancel buttons by default + okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + getShell().setDefaultButton(okButton); + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.window.Window#createContents(dwt.widgets.Composite) + */ + protected Control createContents(final Composite parent) { + final Control[] control = new Control[1]; + BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() { + public void run() { + control[0] = PreferenceDialog.super.createContents(parent); + // Add the first page + selectSavedItem(); + } + }); + + return control[0]; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.dialogs.Dialog#createDialogArea(dwt.widgets.Composite) + */ + protected Control createDialogArea(Composite parent) { + final Composite composite = (Composite) super.createDialogArea(parent); + GridLayout parentLayout = ((GridLayout) composite.getLayout()); + parentLayout.numColumns = 4; + parentLayout.marginHeight = 0; + parentLayout.marginWidth = 0; + parentLayout.verticalSpacing = 0; + parentLayout.horizontalSpacing = 0; + + composite.setBackground(parent.getDisplay().getSystemColor(DWT.COLOR_LIST_BACKGROUND)); + + Control treeControl = createTreeAreaContents(composite); + createSash(composite,treeControl); + + Label versep = new Label(composite, DWT.SEPARATOR | DWT.VERTICAL); + GridData verGd = new GridData(GridData.FILL_VERTICAL | GridData.GRAB_VERTICAL); + + versep.setLayoutData(verGd); + versep.setLayoutData(new GridData(DWT.LEFT, DWT.FILL, false, true)); + + Composite pageAreaComposite = new Composite(composite, DWT.NONE); + pageAreaComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); + GridLayout layout = new GridLayout(1, true); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.verticalSpacing = 0; + pageAreaComposite.setLayout(layout); + + formTitleComposite = new Composite(pageAreaComposite, DWT.NONE); + FormLayout titleLayout = new FormLayout(); + titleLayout.marginWidth = 0; + titleLayout.marginHeight = 0; + formTitleComposite.setLayout(titleLayout); + + GridData titleGridData = new GridData(GridData.FILL_HORIZONTAL); + titleGridData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN; + formTitleComposite.setLayoutData(titleGridData); + + // Build the title area and separator line + Composite titleComposite = new Composite(formTitleComposite, DWT.NONE); + layout = new GridLayout(); + layout.marginBottom = 5; + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.horizontalSpacing = 0; + titleComposite.setLayout(layout); + + FormData titleFormData = new FormData(); + titleFormData.top = new FormAttachment(0,0); + titleFormData.left = new FormAttachment(0,0); + titleFormData.right = new FormAttachment(100,0); + titleFormData.bottom = new FormAttachment(100,0); + + titleComposite.setLayoutData(titleFormData); + createTitleArea(titleComposite); + + Label separator = new Label(pageAreaComposite, DWT.HORIZONTAL | DWT.SEPARATOR); + + separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL)); + + + // Build the Page container + pageContainer = createPageContainer(pageAreaComposite); + GridData pageContainerData = new GridData(GridData.FILL_BOTH); + pageContainerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN; + pageContainer.setLayoutData(pageContainerData); + // Build the separator line + Label bottomSeparator = new Label(parent, DWT.HORIZONTAL | DWT.SEPARATOR); + bottomSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL)); + return composite; + } + + /** + * Create the sash with right control on the right. Note + * that this method assumes GridData for the layout data + * of the rightControl. + * @param composite + * @param rightControl + * @return Sash + * + * @since 3.1 + */ + protected Sash createSash(final Composite composite, final Control rightControl) { + final Sash sash = new Sash(composite, DWT.VERTICAL); + sash.setLayoutData(new GridData(GridData.FILL_VERTICAL)); + sash.setBackground(composite.getDisplay().getSystemColor(DWT.COLOR_LIST_BACKGROUND)); + // the following listener resizes the tree control based on sash deltas. + // If necessary, it will also grow/shrink the dialog. + sash.addListener(DWT.Selection, new Listener() { + /* + * (non-Javadoc) + * + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + */ + public void handleEvent(Event event) { + if (event.detail is DWT.DRAG) { + return; + } + int shift = event.x - sash.getBounds().x; + GridData data = (GridData) rightControl.getLayoutData(); + int newWidthHint = data.widthHint + shift; + if (newWidthHint < 20) { + return; + } + Point computedSize = getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT); + Point currentSize = getShell().getSize(); + // if the dialog wasn't of a custom size we know we can shrink + // it if necessary based on sash movement. + bool customSize = !computedSize.equals(currentSize); + data.widthHint = newWidthHint; + setLastTreeWidth(newWidthHint); + composite.layout(true); + // recompute based on new widget size + computedSize = getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT); + // if the dialog was of a custom size then increase it only if + // necessary. + if (customSize) { + computedSize.x = Math.max(computedSize.x, currentSize.x); + } + computedSize.y = Math.max(computedSize.y, currentSize.y); + if (computedSize.equals(currentSize)) { + return; + } + setShellSize(computedSize.x, computedSize.y); + lastShellSize = getShell().getSize(); + } + }); + return sash; + } + + /** + * Creates the inner page container. + * + * @param parent + * @return Composite + */ + protected Composite createPageContainer(Composite parent) { + + Composite outer = new Composite(parent, DWT.NONE); + + GridData outerData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL + | GridData.GRAB_VERTICAL); + outerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN; + + outer.setLayout(new GridLayout()); + outer.setLayoutData(outerData); + + //Create an outer composite for spacing + scrolled = new ScrolledComposite(outer, DWT.V_SCROLL | DWT.H_SCROLL); + + scrolled.setExpandHorizontal(true); + scrolled.setExpandVertical(true); + + GridData scrolledData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL + | GridData.GRAB_VERTICAL); + + scrolled.setLayoutData(scrolledData); + + Composite result = new Composite(scrolled, DWT.NONE); + + GridData resultData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL + | GridData.GRAB_VERTICAL); + + result.setLayout(getPageLayout()); + result.setLayoutData(resultData); + + scrolled.setContent(result); + + return result; + } + + /** + * Return the layout for the composite that contains + * the pages. + * @return PageLayout + * + * @since 3.1 + */ + protected Layout getPageLayout() { + return new PageLayout(); + } + + /** + * Creates the wizard's title area. + * + * @param parent + * the DWT parent for the title area composite. + * @return the created title area composite. + */ + protected Composite createTitleArea(Composite parent) { + // Create the title area which will contain + // a title, message, and image. + int margins = 2; + titleArea = new Composite(parent, DWT.NONE); + FormLayout layout = new FormLayout(); + layout.marginHeight = 0; + layout.marginWidth = margins; + titleArea.setLayout(layout); + + + GridData layoutData = new GridData(GridData.FILL_HORIZONTAL); + layoutData.verticalAlignment = DWT.TOP; + titleArea.setLayoutData(layoutData); + + // Message label + messageArea = new DialogMessageArea(); + messageArea.createContents(titleArea); + + titleArea.addControlListener(new ControlAdapter() { + /* (non-Javadoc) + * @see dwt.events.ControlAdapter#controlResized(dwt.events.ControlEvent) + */ + public void controlResized(ControlEvent e) { + updateMessage(); + } + }); + + final IPropertyChangeListener fontListener = new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + if (JFaceResources.BANNER_FONT.equals(event.getProperty())) { + updateMessage(); + } + if (JFaceResources.DIALOG_FONT.equals(event.getProperty())) { + updateMessage(); + Font dialogFont = JFaceResources.getDialogFont(); + updateTreeFont(dialogFont); + Control[] children = ((Composite) buttonBar).getChildren(); + for (int i = 0; i < children.length; i++) { + children[i].setFont(dialogFont); + } + } + } + }; + + titleArea.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent event) { + JFaceResources.getFontRegistry().removeListener(fontListener); + } + }); + JFaceResources.getFontRegistry().addListener(fontListener); + messageArea.setTitleLayoutData(createMessageAreaData()); + messageArea.setMessageLayoutData(createMessageAreaData()); + return titleArea; + } + + /** + * Create the layout data for the message area. + * + * @return FormData for the message area. + */ + private FormData createMessageAreaData() { + FormData messageData = new FormData(); + messageData.top = new FormAttachment(0); + messageData.bottom = new FormAttachment(100); + messageData.right = new FormAttachment(100); + messageData.left = new FormAttachment(0); + return messageData; + } + + /** + * @param parent + * the DWT parent for the tree area controls. + * @return the new <code>Control</code>. + * @since 3.0 + */ + protected Control createTreeAreaContents(Composite parent) { + // Build the tree an put it into the composite. + treeViewer = createTreeViewer(parent); + treeViewer.setInput(getPreferenceManager()); + updateTreeFont(JFaceResources.getDialogFont()); + layoutTreeAreaControl(treeViewer.getControl()); + return treeViewer.getControl(); + } + + /** + * Create a new <code>TreeViewer</code>. + * + * @param parent + * the parent <code>Composite</code>. + * @return the <code>TreeViewer</code>. + * @since 3.0 + */ + protected TreeViewer createTreeViewer(Composite parent) { + final TreeViewer viewer = new TreeViewer(parent, DWT.NONE); + addListeners(viewer); + viewer.setLabelProvider(new PreferenceLabelProvider()); + viewer.setContentProvider(new PreferenceContentProvider()); + return viewer; + } + + /** + * Add the listeners to the tree viewer. + * @param viewer + * + * @since 3.1 + */ + protected void addListeners(final TreeViewer viewer) { + viewer.addPostSelectionChangedListener(new ISelectionChangedListener() { + private void handleError() { + try { + // remove the listener temporarily so that the events caused + // by the error handling dont further cause error handling + // to occur. + viewer.removePostSelectionChangedListener(this); + showPageFlippingAbortDialog(); + selectCurrentPageAgain(); + clearSelectedNode(); + } finally { + viewer.addPostSelectionChangedListener(this); + } + } + + public void selectionChanged(SelectionChangedEvent event) { + Object selection = getSingleSelection(event.getSelection()); + if (selection instanceof IPreferenceNode) { + if (!isCurrentPageValid()) { + handleError(); + } else if (!showPage((IPreferenceNode) selection)) { + // Page flipping wasn't successful + handleError(); + } else { + // Everything went well + lastSuccessfulNode = (IPreferenceNode) selection; + } + } + } + }); + ((Tree) viewer.getControl()).addSelectionListener(new SelectionAdapter() { + public void widgetDefaultSelected(final SelectionEvent event) { + ISelection selection = viewer.getSelection(); + if (selection.isEmpty()) { + return; + } + IPreferenceNode singleSelection = getSingleSelection(selection); + bool expanded = viewer.getExpandedState(singleSelection); + viewer.setExpandedState(singleSelection, !expanded); + } + }); + //Register help listener on the tree to use context sensitive help + viewer.getControl().addHelpListener(new HelpListener() { + public void helpRequested(HelpEvent event) { + // call perform help on the current page + if (currentPage !is null) { + currentPage.performHelp(); + } + } + }); + } + + /** + * Find the <code>IPreferenceNode</code> that has data the same id as the + * supplied value. + * + * @param nodeId + * the id to search for. + * @return <code>IPreferenceNode</code> or <code>null</code> if not + * found. + */ + protected IPreferenceNode findNodeMatching(String nodeId) { + List nodes = preferenceManager.getElements(PreferenceManager.POST_ORDER); + for (Iterator i = nodes.iterator(); i.hasNext();) { + IPreferenceNode node = (IPreferenceNode) i.next(); + if (node.getId().equals(nodeId)) { + return node; + } + } + return null; + } + + /** + * Get the last known right side width. + * + * @return the width. + */ + protected int getLastRightWidth() { + return lastTreeWidth; + } + + /** + * Returns the preference mananger used by this preference dialog. + * + * @return the preference mananger + */ + public PreferenceManager getPreferenceManager() { + return preferenceManager; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.preference.IPreferencePageContainer#getPreferenceStore() + */ + public IPreferenceStore getPreferenceStore() { + return preferenceStore; + } + + /** + * Get the name of the selected item preference + * + * @return String + */ + protected String getSelectedNodePreference() { + return lastPreferenceId; + } + + /** + * @param selection + * the <code>ISelection</code> to examine. + * @return the first element, or null if empty. + */ + protected IPreferenceNode getSingleSelection(ISelection selection) { + if (!selection.isEmpty()) { + IStructuredSelection structured = (IStructuredSelection) selection; + if (structured.getFirstElement() instanceof IPreferenceNode) { + return (IPreferenceNode) structured.getFirstElement(); + } + } + return null; + } + + /** + * @return the <code>TreeViewer</code> for this dialog. + * @since 3.3 + */ + public TreeViewer getTreeViewer() { + return treeViewer; + } + + /** + * Save the values specified in the pages. + * <p> + * The default implementation of this framework method saves all pages of + * type <code>PreferencePage</code> (if their store needs saving and is a + * <code>PreferenceStore</code>). + * </p> + * <p> + * Subclasses may override. + * </p> + */ + protected void handleSave() { + Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator(); + while (nodes.hasNext()) { + IPreferenceNode node = (IPreferenceNode) nodes.next(); + IPreferencePage page = node.getPage(); + if (page instanceof PreferencePage) { + // Save now in case tbe workbench does not shutdown cleanly + IPreferenceStore store = ((PreferencePage) page).getPreferenceStore(); + if (store !is null && store.needsSaving() + && store instanceof IPersistentPreferenceStore) { + try { + ((IPersistentPreferenceStore) store).save(); + } catch (IOException e) { + MessageDialog + .openError( + getShell(), + JFaceResources.getString("PreferenceDialog.saveErrorTitle"), //$NON-NLS-1$ + JFaceResources + .format( + "PreferenceDialog.saveErrorMessage", new Object[] { page.getTitle(), e.getMessage() })); //$NON-NLS-1$ + } + } + } + } + } + + /** + * Notifies that the window's close button was pressed, the close menu was + * selected, or the ESCAPE key pressed. + * <p> + * The default implementation of this framework method sets the window's + * return code to <code>CANCEL</code> and closes the window using + * <code>close</code>. Subclasses may extend or reimplement. + * </p> + */ + protected void handleShellCloseEvent() { + // handle the same as pressing cancel + cancelPressed(); + } + + /** + * Notifies of the pressing of the Help button. + * <p> + * The default implementation of this framework method calls + * <code>performHelp</code> on the currently active page. + * </p> + */ + protected void helpPressed() { + if (currentPage !is null) { + currentPage.performHelp(); + } + } + + /** + * Returns whether the current page is valid. + * + * @return <code>false</code> if the current page is not valid, or or + * <code>true</code> if the current page is valid or there is no + * current page + */ + protected bool isCurrentPageValid() { + if (currentPage is null) { + return true; + } + return currentPage.isValid(); + } + + /** + * @param control + * the <code>Control</code> to lay out. + * @since 3.0 + */ + protected void layoutTreeAreaControl(Control control) { + GridData gd = new GridData(GridData.FILL_VERTICAL); + gd.widthHint = getLastRightWidth(); + gd.verticalSpan = 1; + control.setLayoutData(gd); + } + + /** + * The preference dialog implementation of this <code>Dialog</code> + * framework method sends <code>performOk</code> to all pages of the + * preference dialog, then calls <code>handleSave</code> on this dialog to + * save any state, and then calls <code>close</code> to close this dialog. + */ + protected void okPressed() { + SafeRunnable.run(new SafeRunnable() { + private bool errorOccurred; + + /* + * (non-Javadoc) + * + * @see dwtx.core.runtime.ISafeRunnable#run() + */ + public void run() { + getButton(IDialogConstants.OK_ID).setEnabled(false); + errorOccurred = false; + bool hasFailedOK = false; + try { + // Notify all the pages and give them a chance to abort + Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER) + .iterator(); + while (nodes.hasNext()) { + IPreferenceNode node = (IPreferenceNode) nodes.next(); + IPreferencePage page = node.getPage(); + if (page !is null) { + if (!page.performOk()){ + hasFailedOK = true; + return; + } + } + } + } catch (Exception e) { + handleException(e); + } finally { + //Don't bother closing if the OK failed + if(hasFailedOK){ + setReturnCode(FAILED); + getButton(IDialogConstants.OK_ID).setEnabled(true); + return; + } + + if (!errorOccurred) { + //Give subclasses the choice to save the state of the + //preference pages. + handleSave(); + } + setReturnCode(OK); + close(); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) + */ + public void handleException(Throwable e) { + errorOccurred = true; + + Policy.getLog().log(new Status(IStatus.ERROR, Policy.JFACE, 0, e.toString(), e)); + + clearSelectedNode(); + String message = JFaceResources.getString("SafeRunnable.errorMessage"); //$NON-NLS-1$ + MessageDialog.openError(getShell(), JFaceResources.getString("Error"), message); //$NON-NLS-1$ + + } + }); + } + + /** + * Selects the page determined by <code>lastSuccessfulNode</code> in the + * page hierarchy. + */ + void selectCurrentPageAgain() { + if (lastSuccessfulNode is null) { + return; + } + getTreeViewer().setSelection(new StructuredSelection(lastSuccessfulNode)); + currentPage.setVisible(true); + } + + /** + * Selects the saved item in the tree of preference pages. If it cannot do + * this it saves the first one. + */ + protected void selectSavedItem() { + IPreferenceNode node = findNodeMatching(getSelectedNodePreference()); + if (node is null) { + IPreferenceNode[] nodes = preferenceManager.getRootSubNodes(); + ViewerFilter[] filters = getTreeViewer().getFilters(); + for (int i = 0; i < nodes.length; i++) { + IPreferenceNode selectedNode = nodes[i]; + // See if it passes all filters + for (int j = 0; j < filters.length; j++) { + if (!filters[j].select(this.treeViewer, preferenceManager + .getRoot(), selectedNode)) { + selectedNode = null; + break; + } + } + // if it passes all filters select it + if (selectedNode !is null) { + node = selectedNode; + break; + } + } + } + if (node !is null) { + getTreeViewer().setSelection(new StructuredSelection(node), true); + // Keep focus in tree. See bugs 2692, 2621, and 6775. + getTreeViewer().getControl().setFocus(); + } + } + + /** + * Display the given error message. The currently displayed message is saved + * and will be redisplayed when the error message is set to + * <code>null</code>. + * + * @param newErrorMessage + * the errorMessage to display or <code>null</code> + */ + public void setErrorMessage(String newErrorMessage) { + if (newErrorMessage is null) { + messageArea.clearErrorMessage(); + } else { + messageArea.updateText(newErrorMessage, IMessageProvider.ERROR); + } + } + + /** + * Save the last known tree width. + * + * @param width + * the width. + */ + private void setLastTreeWidth(int width) { + lastTreeWidth = width; + } + + /** + * Set the message text. If the message line currently displays an error, + * the message is stored and will be shown after a call to clearErrorMessage + * <p> + * Shortcut for <code>setMessage(newMessage, NONE)</code> + * </p> + * + * @param newMessage + * the message, or <code>null</code> to clear the message + */ + public void setMessage(String newMessage) { + setMessage(newMessage, IMessageProvider.NONE); + } + + /** + * Sets the message for this dialog with an indication of what type of + * message it is. + * <p> + * The valid message types are one of <code>NONE</code>, + * <code>INFORMATION</code>,<code>WARNING</code>, or + * <code>ERROR</code>. + * </p> + * <p> + * Note that for backward compatibility, a message of type + * <code>ERROR</code> is different than an error message (set using + * <code>setErrorMessage</code>). An error message overrides the current + * message until the error message is cleared. This method replaces the + * current message and does not affect the error message. + * </p> + * + * @param newMessage + * the message, or <code>null</code> to clear the message + * @param newType + * the message type + * @since 2.0 + */ + public void setMessage(String newMessage, int newType) { + messageArea.updateText(newMessage, newType); + } + + /** + * Sets the minimum page size. + * + * @param minWidth + * the minimum page width + * @param minHeight + * the minimum page height + * @see #setMinimumPageSize(Point) + */ + public void setMinimumPageSize(int minWidth, int minHeight) { + minimumPageSize.x = minWidth; + minimumPageSize.y = minHeight; + } + + /** + * Sets the minimum page size. + * + * @param size + * the page size encoded as <code>new Point(width,height)</code> + * @see #setMinimumPageSize(int,int) + */ + public void setMinimumPageSize(Point size) { + minimumPageSize.x = size.x; + minimumPageSize.y = size.y; + } + + /** + * Sets the preference store for this preference dialog. + * + * @param store + * the preference store + * @see #getPreferenceStore + */ + public void setPreferenceStore(IPreferenceStore store) { + Assert.isNotNull(store); + preferenceStore = store; + } + + /** + * Save the currently selected node. + */ + private void setSelectedNode() { + String storeValue = null; + IStructuredSelection selection = (IStructuredSelection) getTreeViewer().getSelection(); + if (selection.size() is 1) { + IPreferenceNode node = (IPreferenceNode) selection.getFirstElement(); + storeValue = node.getId(); + } + setSelectedNodePreference(storeValue); + } + + /** + * Sets the name of the selected item preference. Public equivalent to + * <code>setSelectedNodePreference</code>. + * + * @param pageId + * The identifier for the page + * @since 3.0 + */ + public void setSelectedNode(String pageId) { + setSelectedNodePreference(pageId); + } + + /** + * Sets the name of the selected item preference. + * + * @param pageId + * The identifier for the page + */ + protected void setSelectedNodePreference(String pageId) { + lastPreferenceId = pageId; + } + + /** + * Changes the shell size to the given size, ensuring that it is no larger + * than the display bounds. + * + * @param width + * the shell width + * @param height + * the shell height + */ + private void setShellSize(int width, int height) { + Rectangle preferred = getShell().getBounds(); + preferred.width = width; + preferred.height = height; + getShell().setBounds(getConstrainedShellBounds(preferred)); + } + + /** + * Shows the preference page corresponding to the given preference node. + * Does nothing if that page is already current. + * + * @param node + * the preference node, or <code>null</code> if none + * @return <code>true</code> if the page flip was successful, and + * <code>false</code> is unsuccessful + */ + protected bool showPage(IPreferenceNode node) { + if (node is null) { + return false; + } + // Create the page if nessessary + if (node.getPage() is null) { + createPage(node); + } + if (node.getPage() is null) { + return false; + } + IPreferencePage newPage = getPage(node); + if (newPage is currentPage) { + return true; + } + if (currentPage !is null) { + if (!currentPage.okToLeave()) { + return false; + } + } + IPreferencePage oldPage = currentPage; + currentPage = newPage; + // Set the new page's container + currentPage.setContainer(this); + // Ensure that the page control has been created + // (this allows lazy page control creation) + if (currentPage.getControl() is null) { + final bool[] failed = { false }; + SafeRunnable.run(new ISafeRunnable() { + public void handleException(Throwable e) { + failed[0] = true; + } + + public void run() { + createPageControl(currentPage, pageContainer); + } + }); + if (failed[0]) { + return false; + } + // the page is responsible for ensuring the created control is + // accessable + // via getControl. + Assert.isNotNull(currentPage.getControl()); + } + // Force calculation of the page's description label because + // label can be wrapped. + final Point[] size = new Point[1]; + final Point failed = new Point(-1, -1); + SafeRunnable.run(new ISafeRunnable() { + public void handleException(Throwable e) { + size[0] = failed; + } + + public void run() { + size[0] = currentPage.computeSize(); + } + }); + if (size[0].equals(failed)) { + return false; + } + Point contentSize = size[0]; + // Do we need resizing. Computation not needed if the + // first page is inserted since computing the dialog's + // size is done by calling dialog.open(). + // Also prevent auto resize if the user has manually resized + Shell shell = getShell(); + Point shellSize = shell.getSize(); + if (oldPage !is null) { + Rectangle rect = pageContainer.getClientArea(); + Point containerSize = new Point(rect.width, rect.height); + int hdiff = contentSize.x - containerSize.x; + int vdiff = contentSize.y - containerSize.y; + if ((hdiff > 0 || vdiff > 0) && shellSize.equals(lastShellSize)) { + hdiff = Math.max(0, hdiff); + vdiff = Math.max(0, vdiff); + setShellSize(shellSize.x + hdiff, shellSize.y + vdiff); + lastShellSize = shell.getSize(); + if (currentPage.getControl().getSize().x is 0) { + currentPage.getControl().setSize(containerSize); + } + + } else { + currentPage.setSize(containerSize); + } + } + + scrolled.setMinSize(contentSize); + // Ensure that all other pages are invisible + // (including ones that triggered an exception during + // their creation). + Control[] children = pageContainer.getChildren(); + Control currentControl = currentPage.getControl(); + for (int i = 0; i < children.length; i++) { + if (children[i] !is currentControl) { + children[i].setVisible(false); + } + } + // Make the new page visible + currentPage.setVisible(true); + if (oldPage !is null) { + oldPage.setVisible(false); + } + // update the dialog controls + update(); + return true; + } + + /** + * Create the page for the node. + * @param node + * + * @since 3.1 + */ + protected void createPage(IPreferenceNode node) { + node.createPage(); + } + + /** + * Get the page for the node. + * @param node + * @return IPreferencePage + * + * @since 3.1 + */ + protected IPreferencePage getPage(IPreferenceNode node) { + return node.getPage(); + } + + /** + * Shows the "Page Flipping abort" dialog. + */ + void showPageFlippingAbortDialog() { + MessageDialog.openError(getShell(), JFaceResources + .getString("AbortPageFlippingDialog.title"), //$NON-NLS-1$ + JFaceResources.getString("AbortPageFlippingDialog.message")); //$NON-NLS-1$ + } + + /** + * Updates this dialog's controls to reflect the current page. + */ + protected void update() { + // Update the title bar + updateTitle(); + // Update the message line + updateMessage(); + // Update the buttons + updateButtons(); + //Saved the selected node in the preferences + setSelectedNode(); + firePageChanged(new PageChangedEvent(this, getCurrentPage())); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.preference.IPreferencePageContainer#updateButtons() + */ + public void updateButtons() { + okButton.setEnabled(isCurrentPageValid()); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.preference.IPreferencePageContainer#updateMessage() + */ + public void updateMessage() { + String message = null; + String errorMessage = null; + if(currentPage !is null){ + message = currentPage.getMessage(); + errorMessage = currentPage.getErrorMessage(); + } + int messageType = IMessageProvider.NONE; + if (message !is null && currentPage instanceof IMessageProvider) { + messageType = ((IMessageProvider) currentPage).getMessageType(); + } + + if (errorMessage is null){ + if (showingError) { + // we were previously showing an error + showingError = false; + } + } + else { + message = errorMessage; + messageType = IMessageProvider.ERROR; + if (!showingError) { + // we were not previously showing an error + showingError = true; + } + } + messageArea.updateText(message,messageType); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.preference.IPreferencePageContainer#updateTitle() + */ + public void updateTitle() { + if(currentPage is null) { + return; + } + messageArea.showTitle(currentPage.getTitle(), currentPage.getImage()); + } + + /** + * Update the tree to use the specified <code>Font</code>. + * + * @param dialogFont + * the <code>Font</code> to use. + * @since 3.0 + */ + protected void updateTreeFont(Font dialogFont) { + getTreeViewer().getControl().setFont(dialogFont); + } + + /** + * Returns the currentPage. + * @return IPreferencePage + * @since 3.1 + */ + protected IPreferencePage getCurrentPage() { + return currentPage; + } + + /** + * Sets the current page. + * @param currentPage + * + * @since 3.1 + */ + protected void setCurrentPage(IPreferencePage currentPage) { + this.currentPage = currentPage; + } + + /** + * Set the treeViewer. + * @param treeViewer + * + * @since 3.1 + */ + protected void setTreeViewer(TreeViewer treeViewer) { + this.treeViewer = treeViewer; + } + + /** + * Get the composite that is showing the page. + * + * @return Composite. + * + * @since 3.1 + */ + protected Composite getPageContainer() { + return this.pageContainer; + } + + /** + * Set the composite that is showing the page. + * @param pageContainer Composite + * + * @since 3.1 + */ + protected void setPageContainer(Composite pageContainer) { + this.pageContainer = pageContainer; + } + /** + * Create the page control for the supplied page. + * + * @param page - the preference page to be shown + * @param parent - the composite to parent the page + * + * @since 3.1 + */ + protected void createPageControl(IPreferencePage page, Composite parent) { + page.createControl(parent); + } + + /** + * @see dwtx.jface.dialogs.IPageChangeProvider#getSelectedPage() + * + * @since 3.1 + */ + public Object getSelectedPage() { + return getCurrentPage(); + } + + /** + * @see dwtx.jface.dialogs.IPageChangeProvider#addPageChangedListener(dwtx.jface.dialogs.IPageChangedListener) + * @since 3.1 + */ + public void addPageChangedListener(IPageChangedListener listener) { + pageChangedListeners.add(listener); + } + + /** + * @see dwtx.jface.dialogs.IPageChangeProvider#removePageChangedListener(dwtx.jface.dialogs.IPageChangedListener) + * @since 3.1 + */ + public void removePageChangedListener(IPageChangedListener listener) { + pageChangedListeners.remove(listener); + + } + + /** + * Notifies any selection changed listeners that the selected page + * has changed. + * Only listeners registered at the time this method is called are notified. + * + * @param event a selection changed event + * + * @see IPageChangedListener#pageChanged + * + * @since 3.1 + */ + protected void firePageChanged(final PageChangedEvent event) { + Object[] listeners = pageChangedListeners.getListeners(); + for (int i = 0; i < listeners.length; i++) { + final IPageChangedListener l = (IPageChangedListener) listeners[i]; + SafeRunnable.run(new SafeRunnable() { + public void run() { + l.pageChanged(event); + } + }); + } + } +} +++/ \ No newline at end of file