package org.eclipse.jface.preference; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import org.eclipse.jface.dialogs.*; | |
import org.eclipse.jface.dialogs.Dialog; // disambiguate from SWT Dialog | |
import org.eclipse.jface.resource.*; | |
import org.eclipse.jface.util.Assert; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.custom.CLabel; | |
import org.eclipse.swt.custom.BusyIndicator; | |
import org.eclipse.swt.events.*; | |
import org.eclipse.swt.graphics.*; | |
import org.eclipse.swt.layout.*; | |
import org.eclipse.swt.widgets.*; | |
import java.util.*; | |
import java.util.List; // disambiguate from SWT List | |
/** | |
* 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 Dialog implements IPreferencePageContainer { | |
/** | |
* Title area fields | |
*/ | |
public static final String PREF_DLG_TITLE_IMG = "preference_dialog_title_image";//$NON-NLS-1$ | |
public static final String PREF_DLG_IMG_TITLE_ERROR = "preference_dialog_title_error_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$ | |
reg.put(PREF_DLG_IMG_TITLE_ERROR, ImageDescriptor.createFromFile(PreferenceDialog.class, "images/title_error.gif"));//$NON-NLS-1$ | |
} | |
private static final RGB ERROR_BACKGROUND_RGB = new RGB(230, 226, 221); | |
private Composite titleArea; | |
private CLabel messageLabel; | |
private Label titleImage; | |
private Color titleAreaColor; | |
private String message; | |
private Color normalMsgAreaBackground; | |
private Color errorMsgAreaBackground; | |
private Image errorMsgImage; | |
/** | |
* Preference store, initially <code>null</code> meaning none. | |
* | |
* @see #setPreferenceStore | |
*/ | |
private IPreferenceStore preferenceStore; | |
/** | |
* The current preference page, or <code>null</code> if | |
* there is none. | |
*/ | |
private IPreferencePage currentPage; | |
/** | |
* The preference manager. | |
*/ | |
private PreferenceManager preferenceManager; | |
/** | |
* The main control for this dialog. | |
*/ | |
private Composite body; | |
/** | |
* The Composite in which a page is shown. | |
*/ | |
private Composite pageContainer; | |
/** | |
* The minimum page size; 400 by 400 by default. | |
* | |
* @see #setMinimumPageSize | |
*/ | |
private Point minimumPageSize = new Point(400,400); | |
/** | |
* The OK button. | |
*/ | |
private Button okButton; | |
/** | |
* The Cancel button. | |
*/ | |
private Button cancelButton; | |
/** | |
* The Help button; <code>null</code> if none. | |
*/ | |
private Button helpButton = null; | |
/** | |
* Indicates whether help is available; <code>false</code> by default.' | |
* | |
* @see #setHelpAvailable | |
*/ | |
private boolean isHelpAvailable = false; | |
/** | |
* The tree control. | |
*/ | |
private Tree tree; | |
/** | |
* The current tree item. | |
*/ | |
private TreeItem currentTreeItem; | |
/** | |
* Layout for the page container. | |
* | |
* @see #pageContainer | |
*/ | |
private class PageLayout extends Layout { | |
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); | |
} | |
} | |
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); | |
} | |
if (wHint != SWT.DEFAULT) x = wHint; | |
if (hHint != SWT.DEFAULT) y = hHint; | |
return new Point(x, y); | |
} | |
} | |
/** | |
* Creates a new preference dialog under the control of the given preference | |
* manager. | |
* | |
* @param shell the parent shell | |
* @param manager the preference manager | |
*/ | |
public PreferenceDialog(Shell parentShell, PreferenceManager manager) { | |
super(parentShell); | |
preferenceManager = manager; | |
} | |
/* (non-Javadoc) | |
* Method declared on Dialog. | |
*/ | |
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) | |
* Method declared on Dialog. | |
*/ | |
protected void cancelPressed() { | |
// Inform all pages that we are cancelling | |
Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator(); | |
while (nodes.hasNext()) { | |
IPreferenceNode node = (IPreferenceNode) nodes.next(); | |
if (node.getPage() != null) { | |
if(!node.getPage().performCancel()) | |
return; | |
} | |
} | |
setReturnCode(CANCEL); | |
close(); | |
} | |
/* (non-Javadoc) | |
* Method declared on Window. | |
*/ | |
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) | |
* Method declared on Window. | |
*/ | |
protected void configureShell(Shell newShell) { | |
super.configureShell(newShell); | |
newShell.setText(JFaceResources.getString("PreferenceDialog.title"));//$NON-NLS-1$ | |
// Register help listener on the shell | |
newShell.addHelpListener(new HelpListener() { | |
public void helpRequested(HelpEvent event) { | |
// call perform help on the current page | |
if (currentPage != null) { | |
currentPage.performHelp(); | |
} | |
} | |
}); | |
} | |
/* (non-Javadoc) | |
* Method declared on Dialog. | |
*/ | |
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); | |
cancelButton = createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); | |
if (isHelpAvailable) { | |
helpButton = createButton(parent, IDialogConstants.HELP_ID, IDialogConstants.HELP_LABEL, false); | |
} | |
} | |
/* (non-Javadoc) | |
* Method declared on Dialog. | |
*/ | |
protected Control createContents(Composite parent) { | |
Control control = super.createContents(parent); | |
// Add the first page | |
selectFirstItem(); | |
return control; | |
} | |
/* (non-Javadoc) | |
* Method declared on Dialog. | |
*/ | |
protected Control createDialogArea(Composite parent) { | |
GridData gd; | |
Composite composite = (Composite)super.createDialogArea(parent); | |
((GridLayout) composite.getLayout()).numColumns = 2; | |
// Build the tree an put it into the composite. | |
createTree(composite); | |
gd = new GridData(GridData.FILL_VERTICAL); | |
gd.widthHint = 150; | |
gd.verticalSpan = 2; | |
tree.setLayoutData(gd); | |
// Build the title area and separator line | |
Composite titleComposite = new Composite(composite, SWT.NONE); | |
GridLayout layout = new GridLayout(); | |
layout.marginHeight = 0; | |
layout.marginWidth = 0; | |
layout.verticalSpacing = 0; | |
layout.horizontalSpacing = 0; | |
titleComposite.setLayout(layout); | |
titleComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); | |
createTitleArea(titleComposite); | |
Label titleBarSeparator = new Label(titleComposite, SWT.HORIZONTAL | SWT.SEPARATOR); | |
gd = new GridData(GridData.FILL_HORIZONTAL); | |
titleBarSeparator.setLayoutData(gd); | |
// Build the Page container | |
pageContainer = createPageContainer(composite); | |
pageContainer.setLayoutData(new GridData(GridData.FILL_BOTH)); | |
pageContainer.setFont(parent.getFont()); | |
// Build the separator line | |
Label separator = new Label(composite, SWT.HORIZONTAL | SWT.SEPARATOR); | |
gd = new GridData(GridData.FILL_HORIZONTAL); | |
gd.horizontalSpan = 2; | |
separator.setLayoutData(gd); | |
return composite; | |
} | |
/** | |
* Creates the inner page container. | |
*/ | |
private Composite createPageContainer(Composite parent) { | |
Composite result = new Composite(parent, SWT.NULL); | |
result.setLayout(new PageLayout()); | |
return result; | |
} | |
/** | |
* Creates the wizard's title area. | |
* | |
* @param parent the SWT parent for the title area composite | |
* @return the created title area composite | |
*/ | |
private Composite createTitleArea(Composite parent) { | |
// Create the title area which will contain | |
// a title, message, and image. | |
titleArea = new Composite(parent, SWT.NONE); | |
GridLayout layout = new GridLayout(); | |
layout.marginHeight = 0; | |
layout.marginWidth = 0; | |
layout.verticalSpacing = 0; | |
layout.horizontalSpacing = 0; | |
layout.numColumns = 2; | |
titleArea.setLayout(layout); | |
titleArea.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); | |
// Add a dispose listener | |
titleArea.addDisposeListener(new DisposeListener() { | |
public void widgetDisposed(DisposeEvent e) { | |
if (titleAreaColor != null) | |
titleAreaColor.dispose(); | |
if (errorMsgAreaBackground != null) | |
errorMsgAreaBackground.dispose(); | |
} | |
}); | |
// Get the background color for the title area | |
Display display = parent.getDisplay(); | |
Color bg = display.getSystemColor(SWT.COLOR_WHITE); | |
// Message label | |
messageLabel = new CLabel(titleArea, SWT.LEFT); | |
messageLabel.setBackground(bg); | |
messageLabel.setText(" ");//$NON-NLS-1$ | |
GridData gd = new GridData(GridData.FILL_BOTH); | |
messageLabel.setLayoutData(gd); | |
// Title image | |
titleImage = new Label(titleArea, SWT.LEFT); | |
titleImage.setBackground(bg); | |
titleImage.setImage(JFaceResources.getImage(PREF_DLG_TITLE_IMG)); | |
gd = new GridData(); | |
gd.horizontalAlignment = gd.END; | |
titleImage.setLayoutData(gd); | |
return titleArea; | |
} | |
/** | |
* Creates a Tree/TreeItem structure that reflects the page hierarchy. | |
*/ | |
private void createTree(Composite parent) { | |
if (tree != null) | |
tree.dispose(); | |
tree = new Tree(parent, SWT.BORDER); | |
tree.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(final SelectionEvent event) { | |
BusyIndicator.showWhile(tree.getDisplay(),new Runnable(){ | |
public void run() { | |
Object data = event.item.getData(); | |
if (data instanceof IPreferenceNode) { | |
if (!isCurrentPageValid()) { | |
showPageFlippingAbortDialog(); | |
selectCurrentPageAgain(); | |
} else | |
if (!showPage((IPreferenceNode) data)) { | |
// Page flipping wasn't successful | |
showPageFlippingAbortDialog(); | |
selectCurrentPageAgain(); | |
} else { | |
// Everything went well | |
currentTreeItem = (TreeItem) event.item; | |
} | |
} | |
} | |
}); | |
} | |
public void widgetDefaultSelected(final SelectionEvent event) { | |
TreeItem[] selection = tree.getSelection(); | |
if (selection.length > 0) | |
selection[0].setExpanded(!selection[0].getExpanded()); | |
} | |
}); | |
IPreferenceNode node = preferenceManager.getRoot(); | |
IPreferenceNode[] subnodes = node.getSubNodes(); | |
for (int i = 0; i < subnodes.length; i++){ | |
createTreeItemFor(tree, subnodes[i]); | |
} | |
} | |
/** | |
* Creates a TreeItem structure that reflects to the page hierarchy. | |
*/ | |
private void createTreeItemFor(Widget parent, IPreferenceNode node) { | |
TreeItem item = null; | |
if (parent instanceof Tree) | |
item = new TreeItem((Tree) parent, SWT.DEFAULT); | |
else | |
item = new TreeItem((TreeItem) parent, SWT.DEFAULT); | |
item.setData(node); | |
item.setText(node.getLabelText()); | |
Image image = node.getLabelImage(); | |
if (image != null) { | |
item.setImage(image); | |
} | |
IPreferenceNode[] subnodes = node.getSubNodes(); | |
for (int i = 0; i < subnodes.length; i++){ | |
createTreeItemFor(item, subnodes[i]); | |
} | |
} | |
/** | |
* Returns the preference mananger used by this preference dialog. | |
* | |
* @return the preference mananger | |
*/ | |
public PreferenceManager getPreferenceManager() { | |
return preferenceManager; | |
} | |
/* (non-Javadoc) | |
* Method declared on IPreferencePageDialog. | |
*/ | |
public IPreferenceStore getPreferenceStore() { | |
return preferenceStore; | |
} | |
/** | |
* Notifies that the user has pressed the Save button and no page has vetoed. | |
* <p> | |
* The default implementation of this framework method does nothing. | |
* Subclasses should override to save the preferences. | |
* </p> | |
*/ | |
protected void handleSave() { | |
} | |
/** | |
* 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; | |
else | |
return currentPage.isValid(); | |
} | |
/** | |
* 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>hardClose</code> to close | |
* this dialog. | |
*/ | |
protected void okPressed() { | |
// 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(); | |
if (node.getPage() != null) { | |
if(!node.getPage().performOk()) | |
return; | |
} | |
} | |
// Give subclasses the choice to save the state of the | |
// preference pages. | |
handleSave(); | |
close(); | |
} | |
/** | |
* Selects the page determined by <code>currentTreeItem</code> in | |
* the page hierarchy. | |
*/ | |
private void selectCurrentPageAgain() { | |
tree.setSelection(new TreeItem[] { currentTreeItem }); | |
currentPage.setVisible(true); | |
} | |
/** | |
* Selects the first item in the tree of preference pages. | |
*/ | |
protected void selectFirstItem() { | |
if (tree != null) { | |
int count = tree.getItemCount(); | |
if (count > 0) { | |
TreeItem[] items = tree.getItems(); | |
Object data = items[0].getData(); | |
if (data instanceof IPreferenceNode) { | |
tree.setSelection(new TreeItem[] { items[0] }); | |
currentTreeItem = items[0]; | |
showPage((IPreferenceNode) data); | |
} | |
} | |
} | |
} | |
/** | |
* 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 errorMessage the errorMessage to display or <code>null</code> | |
*/ | |
public void setErrorMessage(String errorMessage) { | |
if (errorMessage == null) { | |
if (messageLabel.getImage() != null) { | |
// we were previously showing an error | |
messageLabel.setBackground(normalMsgAreaBackground); | |
messageLabel.setImage(null); | |
titleImage.setImage(JFaceResources.getImage(PREF_DLG_TITLE_IMG)); | |
titleArea.layout(true); | |
} | |
// show the message | |
setMessage(message); | |
} else { | |
messageLabel.setText(errorMessage); | |
if (messageLabel.getImage() == null) { | |
// we were not previously showing an error | |
// lazy initialize the error background color and image | |
if (errorMsgAreaBackground == null) { | |
errorMsgAreaBackground = new Color(messageLabel.getDisplay(), ERROR_BACKGROUND_RGB); | |
errorMsgImage = JFaceResources.getImage(PREF_DLG_IMG_TITLE_ERROR); | |
} | |
// show the error | |
normalMsgAreaBackground = messageLabel.getBackground(); | |
messageLabel.setBackground(errorMsgAreaBackground); | |
messageLabel.setImage(errorMsgImage); | |
titleImage.setImage(null); | |
titleArea.layout(true); | |
} | |
} | |
} | |
/** | |
* 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 | |
*/ | |
public void setMessage(String newMessage) { | |
message = newMessage; | |
if (message == null) | |
message = "";//$NON-NLS-1$ | |
if (messageLabel.getImage() == null) | |
// we are not showing an error | |
messageLabel.setText(message); | |
} | |
/** | |
* Sets the minimum page size. | |
* | |
* @param minWidth the minimum page width | |
* @param minHeight the minimum page height | |
* @see #setMinimumPageSize(org.eclipse.swt.graphics.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; | |
} | |
/** | |
* 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) | |
node.createPage(); | |
if (node.getPage() == null) | |
return false; | |
IPreferencePage newPage = node.getPage(); | |
if (newPage == currentPage) | |
return true; | |
Control currentWindow = null; | |
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) | |
currentPage.createControl(pageContainer); | |
// Force calculation of the page's description label because | |
// label can be wrapped. | |
Point contentSize = currentPage.computeSize(); | |
// 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(). | |
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) { | |
hdiff= Math.max(0, hdiff); | |
vdiff= Math.max(0, vdiff); | |
Shell shell= getShell(); | |
Point shellSize= shell.getSize(); | |
shell.setSize(shellSize.x + hdiff, shellSize.y + vdiff); | |
} else if (hdiff < 0 || vdiff < 0) { | |
newPage.setSize(containerSize); | |
} | |
} | |
// Make the new page visible | |
currentPage.setVisible(true); | |
if (oldPage != null) | |
oldPage.setVisible(false); | |
// update the dialog controls | |
update(); | |
return true; | |
} | |
/** | |
* Shows the "Page Flipping abort" dialog. | |
*/ | |
private void showPageFlippingAbortDialog() { | |
MessageDialog dialog = new MessageDialog(getShell(), JFaceResources.getString("AbortPageFlippingDialog.title"), null, JFaceResources.getString("AbortPageFlippingDialog.message"), MessageDialog.WARNING, new String[] { IDialogConstants.OK_LABEL }, 0);//$NON-NLS-2$//$NON-NLS-1$ | |
dialog.open(); | |
} | |
/** | |
* 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(); | |
} | |
/* (non-Javadoc) | |
* Method declared on IPreferenceContainer | |
*/ | |
public void updateButtons() { | |
okButton.setEnabled(isCurrentPageValid()); | |
} | |
/* (non-Javadoc) | |
* Method declared on IPreferencePageContainer. | |
*/ | |
public void updateMessage() { | |
String pageMessage = currentPage.getMessage(); | |
String pageErrorMessage = currentPage.getErrorMessage(); | |
// Adjust the font | |
if (pageMessage == null && pageErrorMessage == null) | |
messageLabel.setFont(JFaceResources.getBannerFont()); | |
else | |
messageLabel.setFont(JFaceResources.getDialogFont()); | |
// Set the message and error message | |
if (pageMessage == null) { | |
setMessage(currentPage.getTitle()); | |
} else { | |
setMessage(pageMessage); | |
} | |
setErrorMessage(pageErrorMessage); | |
} | |
/* (non-Javadoc) | |
* Method declared on IPreferencePageContainer. | |
*/ | |
public void updateTitle() { | |
updateMessage(); | |
} | |
} |