blob: 8370f142fe61a2e3ccb9377cefdd8c71c6156f1a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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
*******************************************************************************/
package org.eclipse.jface.preference;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
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.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.Assert;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.Policy;
import org.eclipse.jface.util.PropertyChangeEvent;
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.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
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.Listener;
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 {
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);
}
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 = 150;
/**
* 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$
static {
ImageRegistry reg = JFaceResources.getImageRegistry();
reg.put(PREF_DLG_TITLE_IMG, ImageDescriptor.createFromFile(PreferenceDialog.class,
"images/pref_dialog_title.gif")); //$NON-NLS-1$
}
/**
* The current preference page, or <code>null</code> if there is none.
*/
private IPreferencePage currentPage;
private DialogMessageArea messageArea;
/**
* Indicates whether help is available; <code>false</code> by default.'
*
* @see #setHelpAvailable
*/
private boolean isHelpAvailable = false;
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 <code>null</code> meaning none.
*
* @see #setPreferenceStore
*/
private IPreferenceStore preferenceStore;
private Composite titleArea;
/**
* The tree viewer.
*/
private TreeViewer treeViewer;
private ListenerList pageChangedListeners = new ListenerList();
/**
* 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() | SWT.RESIZE | SWT.MAX);
preferenceManager = manager;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.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 org.eclipse.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) != 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 org.eclipse.jface.window.Window#close()
*/
public boolean close() {
List nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER);
for (int i = 0; i < nodes.size(); i++) {
IPreferenceNode node = (IPreferenceNode) nodes.get(i);
node.disposeResources();
}
return super.close();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.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 == null)
lastShellSize = getShell().getSize();
}
});
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#constrainShellSize()
*/
protected void constrainShellSize() {
super.constrainShellSize();
// record opening shell size
if (lastShellSize == null)
lastShellSize = getShell().getSize();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.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);
if (isHelpAvailable) {
createButton(parent, IDialogConstants.HELP_ID, IDialogConstants.HELP_LABEL, false);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.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 org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.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(SWT.COLOR_LIST_BACKGROUND));
Control treeControl = createTreeAreaContents(composite);
createSash(composite,treeControl);
Label versep = new Label(composite, SWT.SEPARATOR | SWT.VERTICAL);
GridData verGd = new GridData(GridData.FILL_VERTICAL | GridData.GRAB_VERTICAL);
versep.setLayoutData(verGd);
versep.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
Composite pageAreaComposite = new Composite(composite, SWT.NONE);
pageAreaComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
GridLayout layout = new GridLayout(1, true);
layout.marginHeight = 0;
layout.marginWidth = 0;
pageAreaComposite.setLayout(layout);
// Build the title area and separator line
Composite titleComposite = new Composite(pageAreaComposite, SWT.NONE);
layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
layout.horizontalSpacing = 0;
titleComposite.setLayout(layout);
GridData titleLayoutData = new GridData(GridData.FILL_HORIZONTAL);
titleLayoutData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
titleComposite.setLayoutData(titleLayoutData);
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, new Listener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent(Event 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) {
//Create an outer composite for spacing
Composite outer = new Composite(parent, SWT.NONE);
GridData outerData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL
| GridData.GRAB_VERTICAL);
outer.setLayout(new GridLayout());
outer.setLayoutData(outerData);
Composite result = new Composite(outer, SWT.NONE);
GridData resultData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL
| GridData.GRAB_VERTICAL);
result.setLayout(getPageLayout());
result.setLayoutData(resultData);
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() {
/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.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 SWT 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, 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);
}
}
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);
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() {
public void helpRequested(HelpEvent event) {
// call perform help on the current page
if (currentPage != 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 org.eclipse.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.0
*/
protected 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 != 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 != 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 boolean isCurrentPageValid() {
if (currentPage == 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 boolean errorOccurred;
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.ISafeRunnable#run()
*/
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 = (IPreferenceNode) 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)
return;
if (!errorOccurred)
//Give subclasses the choice to save the state of the
//preference pages.
handleSave();
close();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.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 == 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.getRoot().getSubNodes();
if (nodes.length > 0)
node = nodes[0];
}
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
* <code>null</code>.
*
* @param newErrorMessage
* the errorMessage to display or <code>null</code>
*/
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;
}
/**
* Sets whether a Help button is available for this dialog.
* <p>
* Clients must call this framework method before the dialog's control has
* been created.
* <p>
*
* @param b
* <code>true</code> to include a Help button, and
* <code>false</code> to not include one (the default)
*/
public void setHelpAvailable(boolean b) {
isHelpAvailable = b;
}
/**
* 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() == 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 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() {
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 != 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 //Set the size to be sure we use the result of computeSize
currentPage.setSize(containerSize);
}
// 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.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 org.eclipse.jface.preference.IPreferencePageContainer#updateButtons()
*/
public void updateButtons() {
okButton.setEnabled(isCurrentPageValid());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.preference.IPreferencePageContainer#updateMessage()
*/
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);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.preference.IPreferencePageContainer#updateTitle()
*/
public void updateTitle() {
if(currentPage == 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 org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage()
*
* @since 3.1
*/
public Object getSelectedPage() {
return getCurrentPage();
}
/**
* @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
* @since 3.1
*/
public void addPageChangedListener(IPageChangedListener listener) {
pageChangedListeners.add(listener);
}
/**
* @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.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);
}
});
}
}
}