All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.preference.PreferenceDialog Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2013, 2015 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 
 *     	- Bug 188056 [Preferences] PreferencePages have to less indent in PreferenceDialog
 *     Stefan Xenos  - Bug 466793
 *     Jan-Ove Weichel  - Bug 475879
 *******************************************************************************/
package org.eclipse.jface.preference;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.DialogMessageArea;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.IPageChangeProvider;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.Policy;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;

/**
 * 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 {
		@Override
		public Point computeSize(Composite composite, int wHint, int hHint, boolean force) {
			if (wHint != SWT.DEFAULT && hHint != SWT.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(SWT.DEFAULT, SWT.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 != null){
				Point size = currentPage.computeSize();
				x = Math.max(x, size.x);
				y = Math.max(y, size.y);
			}

			if (wHint != SWT.DEFAULT) {
				x = wHint;
			}
			if (hHint != SWT.DEFAULT) {
				y = hHint;
			}
			return new Point(x, y);
		}

		@Override
		public void layout(Composite composite, boolean 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 null 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 boolean showingError = false;

	/**
	 * Preference store, initially null 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);
		preferenceManager = manager;
	}

	@Override
	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;
		}
		}
	}

	@Override
	protected void cancelPressed() {
		// Inform all pages that we are cancelling
		Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator();
		final boolean[] cancelOK = new boolean[] { true };
		while (nodes.hasNext()) {
			final IPreferenceNode node = nodes.next();
			if (getPage(node) != null) {
				SafeRunnable.run(new SafeRunnable() {
					@Override
					public void run() {
						if (!getPage(node).performCancel()) {
							cancelOK[0] = false;
						}
					}
				});
				if (!cancelOK[0])
					return;
			}
		}

		// Give subclasses the choice to save the state of the preference pages if needed
		handleSave();

		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);
	}

	@Override
	public boolean close() {

		//Do this is in a SafeRunnable as it may run client code
		SafeRunnable runnable = new SafeRunnable(){
			@Override
			public void run() throws Exception {
				List nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER);
				for (int i = 0; i < nodes.size(); i++) {
					IPreferenceNode node = nodes.get(i);
					node.disposeResources();
				}

			}

			@Override
			public void handleException(Throwable e) {
				super.handleException(e);
				clearSelectedNode();//Do not cache a node with problems
			}
		};

		SafeRunner.run(runnable);

		return super.close();
	}

	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText(JFaceResources.getString("PreferenceDialog.title")); //$NON-NLS-1$
		newShell.addShellListener(new ShellAdapter() {
			@Override
			public void shellActivated(ShellEvent e) {
				if (lastShellSize == null) {
					lastShellSize = getShell().getSize();
				}
			}

		});

	}

	@Override
	protected void constrainShellSize() {
		super.constrainShellSize();
		// record opening shell size
		if (lastShellSize == null) {
			lastShellSize = getShell().getSize();
		}
	}

	@Override
	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);
	}

	@Override
	protected Control createContents(final Composite parent) {
		final Control[] control = new Control[1];
		BusyIndicator.showWhile(getShell().getDisplay(), () -> {
			control[0] = PreferenceDialog.super.createContents(parent);
			// Add the first page
			selectSavedItem();
		});

		return control[0];
	}

	@Override
	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(SWT.COLOR_LIST_BACKGROUND));

		Control treeControl = createTreeAreaContents(composite);
		createSash(composite,treeControl);

		Label versep = new Label(composite, SWT.SEPARATOR | SWT.VERTICAL);
		GridDataFactory.fillDefaults().grab(false, true).applyTo(versep);

		Composite pageAreaComposite = new Composite(composite, SWT.NONE);
		GridDataFactory.fillDefaults().grab(true, true).applyTo(pageAreaComposite);
		GridLayout layout = new GridLayout(1, true);
		layout.marginHeight = 0;
		layout.marginWidth = 0;
		layout.verticalSpacing = 0;
		pageAreaComposite.setLayout(layout);

		formTitleComposite = new Composite(pageAreaComposite, SWT.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, SWT.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, SWT.HORIZONTAL | SWT.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, SWT.HORIZONTAL | SWT.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, SWT.VERTICAL);
		sash.setLayoutData(new GridData(GridData.FILL_VERTICAL));
		sash.setBackground(composite.getDisplay().getSystemColor(SWT.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(SWT.Selection, event -> {
			if (event.detail == SWT.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(SWT.DEFAULT, SWT.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.
			boolean customSize = !computedSize.equals(currentSize);
			data.widthHint = newWidthHint;
			setLastTreeWidth(newWidthHint);
			composite.layout(true);
			// recompute based on new widget size
			computedSize = getShell().computeSize(SWT.DEFAULT, SWT.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, SWT.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, SWT.V_SCROLL | SWT.H_SCROLL);

		// always show the focus control
		scrolled.setShowFocusedControl(true);
		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, SWT.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 SWT 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, SWT.NONE);
		FormLayout layout = new FormLayout();
		layout.marginHeight = 0;
		layout.marginWidth = margins;
		titleArea.setLayout(layout);


		GridData layoutData = new GridData(GridData.FILL_HORIZONTAL);
		layoutData.verticalAlignment = SWT.TOP;
		titleArea.setLayoutData(layoutData);

		// Message label
		messageArea = new DialogMessageArea();
		messageArea.createContents(titleArea);

		titleArea.addControlListener(new ControlAdapter() {
			@Override
			public void controlResized(ControlEvent e) {
				updateMessage();
			}
		});

		final IPropertyChangeListener fontListener = 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(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 SWT parent for the tree area controls.
	 * @return the new Control.
	 * @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 TreeViewer.
	 *
	 * @param parent
	 *            the parent Composite.
	 * @return the TreeViewer.
	 * @since 3.0
	 */
	protected TreeViewer createTreeViewer(Composite parent) {
		final TreeViewer viewer = new TreeViewer(parent, SWT.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);
				}
			}

			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				final Object selection = getSingleSelection(event.getSelection());
				if (selection instanceof IPreferenceNode) {
					BusyIndicator.showWhile(getShell().getDisplay(), () -> {
						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() {
			@Override
			public void widgetDefaultSelected(final SelectionEvent event) {
				ISelection selection = viewer.getSelection();
				if (selection.isEmpty()) {
					return;
				}
				IPreferenceNode singleSelection = getSingleSelection(selection);
				boolean expanded = viewer.getExpandedState(singleSelection);
				viewer.setExpandedState(singleSelection, !expanded);
			}
		});
		//Register help listener on the tree to use context sensitive help
		viewer.getControl().addHelpListener(new HelpListener() {
			@Override
			public void helpRequested(HelpEvent event) {
				if (currentPage == null) { // no current page? open dialog's help
					openDialogHelp();
					return;
				}
				// A) A typical path: the current page has registered its own help link
				// via WorkbenchHelpSystem#setHelp(). When just call it and let
				// it handle the help request.
				Control pageControl = currentPage.getControl();
				if (pageControl != null && pageControl.isListening(SWT.Help)) {
					currentPage.performHelp();
					return;
				}

				// B) Less typical path: no standard listener has been created for the page.
				// In this case we may or may not have an override of page's #performHelp().
				// 1) Try to get default help opened for the dialog;
				openDialogHelp();
				// 2) Next call currentPage's #performHelp(). If it was overridden, it might switch help
				// to something else.
				currentPage.performHelp();
			}

			private void openDialogHelp() {
				if (pageContainer == null)
					return;
		    	for(Control currentControl = pageContainer; currentControl != null; currentControl = currentControl.getParent()) {
		    		if (currentControl.isListening(SWT.Help)) {
		    			currentControl.notifyListeners(SWT.Help, new Event());
		    			break;
		    		}
		    	}
			}
		});
	}

	/**
	 * Find the IPreferenceNode that has data the same id as the
	 * supplied value.
	 *
	 * @param nodeId
	 *            the id to search for.
	 * @return IPreferenceNode or null if not
	 *         found.
	 */
	protected IPreferenceNode findNodeMatching(String nodeId) {
		List nodes = preferenceManager.getElements(PreferenceManager.POST_ORDER);
		for (Iterator i = nodes.iterator(); i.hasNext();) {
			IPreferenceNode node = 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;
	}

	@Override
	public IPreferenceStore getPreferenceStore() {
		return preferenceStore;
	}

	/**
	 * Get the name of the selected item preference
	 *
	 * @return String
	 */
	protected String getSelectedNodePreference() {
		return lastPreferenceId;
	}

	/**
	 * @param selection
	 *            the ISelection 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 TreeViewer for this dialog.
	 * @since 3.3
	 */
	public TreeViewer getTreeViewer() {
		return treeViewer;
	}

	/**
	 * Save the values specified in the pages.
	 * 

* The default implementation of this framework method saves all pages of * type PreferencePage (if their store needs saving and is a * PreferenceStore). *

*

* Subclasses may override. *

*/ protected void handleSave() { Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator(); while (nodes.hasNext()) { IPreferenceNode node = 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 != null && store.needsSaving() && store instanceof IPersistentPreferenceStore) { try { ((IPersistentPreferenceStore) store).save(); } catch (IOException e) { String message = JFaceResources.format("PreferenceDialog.saveErrorMessage", page.getTitle(), //$NON-NLS-1$ e.getMessage()); Policy.getStatusHandler().show( new Status(IStatus.ERROR, Policy.JFACE, message, e), JFaceResources.getString("PreferenceDialog.saveErrorTitle")); //$NON-NLS-1$ } } } } } /** * Notifies that the window's close button was pressed, the close menu was * selected, or the ESCAPE key pressed. *

* The default implementation of this framework method sets the window's * return code to CANCEL and closes the window using * close. Subclasses may extend or reimplement. *

*/ @Override protected void handleShellCloseEvent() { // handle the same as pressing cancel cancelPressed(); } /** * Notifies of the pressing of the Help button. *

* The default implementation of this framework method calls * performHelp on the currently active page. *

*/ protected void helpPressed() { if (currentPage != null) { currentPage.performHelp(); } } /** * Returns whether the current page is valid. * * @return false if the current page is not valid, or or * true if the current page is valid or there is no * current page */ protected boolean isCurrentPageValid() { if (currentPage == null) { return true; } return currentPage.isValid(); } /** * @param control * the Control to lay out. * @since 3.0 */ protected void layoutTreeAreaControl(Control control) { GridDataFactory.fillDefaults().hint(getLastRightWidth(), convertVerticalDLUsToPixels(120)).grab(false, true) .applyTo(control); } /** * The preference dialog implementation of this Dialog * framework method sends performOk to all pages of the * preference dialog, then calls handleSave on this dialog to * save any state, and then calls close to close this dialog. */ @Override protected void okPressed() { SafeRunnable.run(new SafeRunnable() { private boolean errorOccurred; @Override public void run() { getButton(IDialogConstants.OK_ID).setEnabled(false); errorOccurred = false; boolean 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 = nodes.next(); IPreferencePage page = node.getPage(); if (page != 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(); } } @Override 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$ Policy.getStatusHandler().show( new Status(IStatus.ERROR, Policy.JFACE, message, e), JFaceResources.getString("Error")); //$NON-NLS-1$ } }); } /** * Selects the page determined by lastSuccessfulNode in the * page hierarchy. */ void selectCurrentPageAgain() { if (lastSuccessfulNode == 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 == null) { IPreferenceNode[] nodes = preferenceManager.getRootSubNodes(); ViewerComparator comparator = getTreeViewer().getComparator(); if (comparator != null) { comparator.sort(null, nodes); } 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 != null) { node = selectedNode; break; } } } if (node != 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 * null. * * @param newErrorMessage * the errorMessage to display or null */ public void setErrorMessage(String newErrorMessage) { if (newErrorMessage == 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 *

* Shortcut for setMessage(newMessage, NONE) *

* * @param newMessage * the message, or null 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. *

* The valid message types are one of NONE, * INFORMATION,WARNING, or * ERROR. *

*

* Note that for backward compatibility, a message of type * ERROR is different than an error message (set using * setErrorMessage). 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. *

* * @param newMessage * the message, or null 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 new Point(width,height) * @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() == 1) { IPreferenceNode node = (IPreferenceNode) selection.getFirstElement(); storeValue = node.getId(); } setSelectedNodePreference(storeValue); } /** * Sets the name of the selected item preference. Public equivalent to * setSelectedNodePreference. * * @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 null if none * @return true if the page flip was successful, and * false is unsuccessful */ protected boolean showPage(IPreferenceNode node) { if (node == null) { return false; } // Create the page if nessessary if (node.getPage() == null) { createPage(node); } if (node.getPage() == null) { return false; } IPreferencePage newPage = getPage(node); if (newPage == currentPage) { return true; } if (currentPage != 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() == null) { final boolean[] failed = { false }; SafeRunnable.run(new ISafeRunnable() { @Override public void handleException(Throwable e) { failed[0] = true; } @Override 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() { @Override public void handleException(Throwable e) { size[0] = failed; } @Override 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 != 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 == 0) { currentPage.getControl().setSize(containerSize); } } else { currentPage.getControl().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] != currentControl) { children[i].setVisible(false); } } // Make the new page visible currentPage.setVisible(true); if (oldPage != 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.open(MessageDialog.ERROR, getShell(), JFaceResources .getString("AbortPageFlippingDialog.title"), //$NON-NLS-1$ JFaceResources.getString("AbortPageFlippingDialog.message"), SWT.SHEET); //$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())); } @Override public void updateButtons() { okButton.setEnabled(isCurrentPageValid()); } @Override public void updateMessage() { String message = null; String errorMessage = null; if(currentPage != null){ message = currentPage.getMessage(); errorMessage = currentPage.getErrorMessage(); } int messageType = IMessageProvider.NONE; if (message != null && currentPage instanceof IMessageProvider) { messageType = ((IMessageProvider) currentPage).getMessageType(); } if (errorMessage == 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); } @Override public void updateTitle() { if(currentPage == null) { return; } messageArea.showTitle(currentPage.getTitle(), currentPage.getImage()); } /** * Update the tree to use the specified Font. * * @param dialogFont * the Font 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 org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage() * * @since 3.1 */ @Override public Object getSelectedPage() { return getCurrentPage(); } /** * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) * @since 3.1 */ @Override public void addPageChangedListener(IPageChangedListener listener) { pageChangedListeners.add(listener); } /** * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) * @since 3.1 */ @Override 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() { @Override public void run() { l.pageChanged(event); } }); } } @Override protected boolean isResizable() { return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy