0001: package net.refractions.udig.style.sld.editor.internal;
0002:
0003: /*******************************************************************************
0004: * Copyright (c) 2000, 2005 IBM Corporation and others.
0005: * All rights reserved. This program and the accompanying materials
0006: * are made available under the terms of the Eclipse Public License v1.0
0007: * which accompanies this distribution, and is available at
0008: * http://www.eclipse.org/legal/epl-v10.html
0009: *
0010: * Contributors:
0011: * IBM Corporation - initial API and implementation
0012: *******************************************************************************/
0013:
0014: import java.util.Iterator;
0015: import java.util.List;
0016:
0017: import net.refractions.udig.style.sld.IEditorPage;
0018: import net.refractions.udig.style.sld.IEditorPageContainer;
0019: import net.refractions.udig.style.sld.SLDPlugin;
0020: import net.refractions.udig.style.sld.editor.EditorPageManager;
0021:
0022: import org.eclipse.core.runtime.ISafeRunnable;
0023: import org.eclipse.core.runtime.IStatus;
0024: import org.eclipse.core.runtime.Status;
0025: import org.eclipse.jface.dialogs.Dialog;
0026: import org.eclipse.jface.dialogs.DialogMessageArea;
0027: import org.eclipse.jface.dialogs.IDialogConstants;
0028: import org.eclipse.jface.dialogs.IMessageProvider;
0029: import org.eclipse.jface.dialogs.IPageChangeProvider;
0030: import org.eclipse.jface.dialogs.IPageChangedListener;
0031: import org.eclipse.jface.dialogs.MessageDialog;
0032: import org.eclipse.jface.dialogs.PageChangedEvent;
0033: import org.eclipse.jface.resource.ImageDescriptor;
0034: import org.eclipse.jface.resource.ImageRegistry;
0035: import org.eclipse.jface.resource.JFaceResources;
0036: import org.eclipse.jface.util.Assert;
0037: import org.eclipse.jface.util.IPropertyChangeListener;
0038: import org.eclipse.jface.util.ListenerList;
0039: import org.eclipse.jface.util.PropertyChangeEvent;
0040: import org.eclipse.jface.util.SafeRunnable;
0041: import org.eclipse.jface.viewers.ISelection;
0042: import org.eclipse.jface.viewers.ISelectionChangedListener;
0043: import org.eclipse.jface.viewers.IStructuredSelection;
0044: import org.eclipse.jface.viewers.SelectionChangedEvent;
0045: import org.eclipse.jface.viewers.StructuredSelection;
0046: import org.eclipse.jface.viewers.TreeViewer;
0047: import org.eclipse.swt.SWT;
0048: import org.eclipse.swt.custom.BusyIndicator;
0049: import org.eclipse.swt.events.ControlAdapter;
0050: import org.eclipse.swt.events.ControlEvent;
0051: import org.eclipse.swt.events.DisposeEvent;
0052: import org.eclipse.swt.events.DisposeListener;
0053: import org.eclipse.swt.events.HelpEvent;
0054: import org.eclipse.swt.events.HelpListener;
0055: import org.eclipse.swt.events.SelectionAdapter;
0056: import org.eclipse.swt.events.SelectionEvent;
0057: import org.eclipse.swt.events.ShellAdapter;
0058: import org.eclipse.swt.events.ShellEvent;
0059: import org.eclipse.swt.graphics.Font;
0060: import org.eclipse.swt.graphics.Point;
0061: import org.eclipse.swt.graphics.Rectangle;
0062: import org.eclipse.swt.layout.FormAttachment;
0063: import org.eclipse.swt.layout.FormData;
0064: import org.eclipse.swt.layout.FormLayout;
0065: import org.eclipse.swt.layout.GridData;
0066: import org.eclipse.swt.layout.GridLayout;
0067: import org.eclipse.swt.widgets.Button;
0068: import org.eclipse.swt.widgets.Composite;
0069: import org.eclipse.swt.widgets.Control;
0070: import org.eclipse.swt.widgets.Event;
0071: import org.eclipse.swt.widgets.Label;
0072: import org.eclipse.swt.widgets.Layout;
0073: import org.eclipse.swt.widgets.Listener;
0074: import org.eclipse.swt.widgets.Sash;
0075: import org.eclipse.swt.widgets.Shell;
0076: import org.eclipse.swt.widgets.Tree;
0077:
0078: /**
0079: * A preference dialog is a hierarchical presentation of preference pages. Each
0080: * page is represented by a node in the tree shown on the left hand side of the
0081: * dialog; when a node is selected, the corresponding page is shown on the right
0082: * hand side.
0083: */
0084: public class EditorDialog extends Dialog implements
0085: IEditorPageContainer, IPageChangeProvider {
0086: /**
0087: * Layout for the page container.
0088: *
0089: */
0090: private class PageLayout extends Layout {
0091: @Override
0092: public Point computeSize(Composite composite, int wHint,
0093: int hHint, boolean force) {
0094: if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT)
0095: return new Point(wHint, hHint);
0096: int x = minimumPageSize.x;
0097: int y = minimumPageSize.y;
0098: Control[] children = composite.getChildren();
0099: for (int i = 0; i < children.length; i++) {
0100: Point size = children[i].computeSize(SWT.DEFAULT,
0101: SWT.DEFAULT, force);
0102: x = Math.max(x, size.x);
0103: y = Math.max(y, size.y);
0104: }
0105:
0106: //As pages can implement thier own computeSize
0107: //take it into account
0108: if (currentPage != null) {
0109: Point size = currentPage.computeSize();
0110: x = Math.max(x, size.x);
0111: y = Math.max(y, size.y);
0112: }
0113:
0114: if (wHint != SWT.DEFAULT)
0115: x = wHint;
0116: if (hHint != SWT.DEFAULT)
0117: y = hHint;
0118: return new Point(x, y);
0119: }
0120:
0121: @Override
0122: public void layout(Composite composite, boolean force) {
0123: Rectangle rect = composite.getClientArea();
0124: Control[] children = composite.getChildren();
0125: for (int i = 0; i < children.length; i++) {
0126: children[i].setSize(rect.width, rect.height);
0127: }
0128: }
0129: }
0130:
0131: //The id of the last page that was selected
0132: private static String lastPageId = null;
0133:
0134: //The last known tree width
0135: private static int lastTreeWidth = 150;
0136:
0137: /**
0138: * Indentifier for the error image
0139: */
0140: public static final String PREF_DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR;
0141:
0142: /**
0143: * Title area fields
0144: */
0145: public static final String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$
0146: static {
0147: ImageRegistry reg = JFaceResources.getImageRegistry();
0148: reg.put(PREF_DLG_TITLE_IMG, ImageDescriptor.createFromFile(
0149: EditorDialog.class, "images/pref_dialog_title.gif")); //$NON-NLS-1$
0150: }
0151:
0152: /**
0153: * The current preference page, or <code>null</code> if there is none.
0154: */
0155: private IEditorPage currentPage;
0156:
0157: private DialogMessageArea messageArea;
0158:
0159: /**
0160: * Indicates whether help is available; <code>false</code> by default.'
0161: *
0162: * @see #setHelpAvailable
0163: */
0164: private boolean isHelpAvailable = false;
0165:
0166: private Point lastShellSize;
0167:
0168: private IEditorNode lastSuccessfulNode;
0169:
0170: /**
0171: * The minimum page size; 400 by 400 by default.
0172: *
0173: * @see #setMinimumPageSize(Point)
0174: */
0175: private Point minimumPageSize = new Point(400, 400);
0176:
0177: /**
0178: * The OK button.
0179: */
0180: private Button okButton;
0181:
0182: /**
0183: * The Composite in which a page is shown.
0184: */
0185: private Composite pageContainer;
0186:
0187: /**
0188: * The preference manager.
0189: */
0190: private EditorPageManager editorPageManager;
0191:
0192: /**
0193: * Flag for the presence of the error message.
0194: */
0195: private boolean showingError = false;
0196:
0197: private Composite titleArea;
0198:
0199: /**
0200: * The tree viewer.
0201: */
0202: private TreeViewer treeViewer;
0203:
0204: private ListenerList pageChangedListeners = new ListenerList(3);
0205:
0206: /**
0207: * Creates a new preference dialog under the control of the given preference
0208: * manager.
0209: *
0210: * @param parentShell
0211: * the parent shell
0212: * @param manager
0213: * the preference manager
0214: */
0215: public EditorDialog(Shell parentShell, EditorPageManager manager) {
0216: super (parentShell);
0217: setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
0218: editorPageManager = manager;
0219: }
0220:
0221: /*
0222: * (non-Javadoc)
0223: *
0224: * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
0225: */
0226: @Override
0227: protected void buttonPressed(int buttonId) {
0228: switch (buttonId) {
0229: case IDialogConstants.OK_ID: {
0230: okPressed();
0231: return;
0232: }
0233: case IDialogConstants.CANCEL_ID: {
0234: cancelPressed();
0235: return;
0236: }
0237: case IDialogConstants.HELP_ID: {
0238: helpPressed();
0239: return;
0240: }
0241: }
0242: }
0243:
0244: /*
0245: * (non-Javadoc)
0246: *
0247: * @see org.eclipse.jface.dialogs.Dialog#cancelPressed()
0248: */
0249: @Override
0250: protected void cancelPressed() {
0251: // Inform all pages that we are cancelling
0252: Iterator nodes = editorPageManager.getElements(
0253: EditorPageManager.PRE_ORDER).iterator();
0254: while (nodes.hasNext()) {
0255: final IEditorNode node = (IEditorNode) nodes.next();
0256: if (getPage(node) != null) {
0257: SafeRunnable.run(new SafeRunnable() {
0258: public void run() {
0259: if (!getPage(node).performCancel())
0260: return;
0261: }
0262: });
0263: }
0264: }
0265: setReturnCode(CANCEL);
0266: close();
0267: }
0268:
0269: /**
0270: * Clear the last selected node. This is so that we not chache the last
0271: * selection in case of an error.
0272: */
0273: void clearSelectedNode() {
0274: setSelectedNode(null);
0275: }
0276:
0277: /*
0278: * (non-Javadoc)
0279: *
0280: * @see org.eclipse.jface.window.Window#close()
0281: */
0282: @Override
0283: public boolean close() {
0284: List nodes = editorPageManager
0285: .getElements(EditorPageManager.PRE_ORDER);
0286: for (int i = 0; i < nodes.size(); i++) {
0287: IEditorNode node = (IEditorNode) nodes.get(i);
0288: node.disposeResources();
0289: }
0290: return super .close();
0291: }
0292:
0293: /*
0294: * (non-Javadoc)
0295: *
0296: * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
0297: */
0298: @Override
0299: protected void configureShell(Shell newShell) {
0300: super .configureShell(newShell);
0301: newShell
0302: .setText(JFaceResources.getString("EditorDialog.title")); //$NON-NLS-1$
0303: newShell.addShellListener(new ShellAdapter() {
0304: @Override
0305: public void shellActivated(ShellEvent e) {
0306: if (lastShellSize == null)
0307: lastShellSize = getShell().getSize();
0308: }
0309:
0310: });
0311:
0312: }
0313:
0314: /*
0315: * (non-Javadoc)
0316: *
0317: * @see org.eclipse.jface.window.Window#constrainShellSize()
0318: */
0319: @Override
0320: protected void constrainShellSize() {
0321: super .constrainShellSize();
0322: // record opening shell size
0323: if (lastShellSize == null)
0324: lastShellSize = getShell().getSize();
0325: }
0326:
0327: /*
0328: * (non-Javadoc)
0329: *
0330: * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
0331: */
0332: @Override
0333: protected void createButtonsForButtonBar(Composite parent) {
0334: // create OK and Cancel buttons by default
0335: okButton = createButton(parent, IDialogConstants.OK_ID,
0336: IDialogConstants.OK_LABEL, true);
0337: getShell().setDefaultButton(okButton);
0338: createButton(parent, IDialogConstants.CANCEL_ID,
0339: IDialogConstants.CANCEL_LABEL, false);
0340: if (isHelpAvailable) {
0341: createButton(parent, IDialogConstants.HELP_ID,
0342: IDialogConstants.HELP_LABEL, false);
0343: }
0344: }
0345:
0346: /*
0347: * (non-Javadoc)
0348: *
0349: * @see org.eclipse.jface.window.Window#createContents(org.eclipse.swt.widgets.Composite)
0350: */
0351: @Override
0352: protected Control createContents(final Composite parent) {
0353: final Control[] control = new Control[1];
0354: BusyIndicator.showWhile(getShell().getDisplay(),
0355: new Runnable() {
0356: public void run() {
0357: control[0] = EditorDialog.super
0358: .createContents(parent);
0359: // Add the first page
0360: selectSavedItem();
0361: }
0362: });
0363:
0364: return control[0];
0365: }
0366:
0367: /*
0368: * (non-Javadoc)
0369: *
0370: * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
0371: */
0372: @Override
0373: protected Control createDialogArea(Composite parent) {
0374: final Composite composite = (Composite) super
0375: .createDialogArea(parent);
0376: GridLayout parentLayout = ((GridLayout) composite.getLayout());
0377: parentLayout.numColumns = 4;
0378: parentLayout.marginHeight = 0;
0379: parentLayout.marginWidth = 0;
0380: parentLayout.verticalSpacing = 0;
0381: parentLayout.horizontalSpacing = 0;
0382:
0383: composite.setBackground(parent.getDisplay().getSystemColor(
0384: SWT.COLOR_LIST_BACKGROUND));
0385:
0386: Control treeControl = createTreeAreaContents(composite);
0387: createSash(composite, treeControl);
0388:
0389: Label versep = new Label(composite, SWT.SEPARATOR
0390: | SWT.VERTICAL);
0391: GridData verGd = new GridData(GridData.FILL_VERTICAL
0392: | GridData.GRAB_VERTICAL);
0393:
0394: versep.setLayoutData(verGd);
0395: versep.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false,
0396: true));
0397:
0398: Composite pageAreaComposite = new Composite(composite, SWT.NONE);
0399: pageAreaComposite
0400: .setLayoutData(new GridData(GridData.FILL_BOTH));
0401: GridLayout layout = new GridLayout(1, true);
0402: layout.marginHeight = 0;
0403: layout.marginWidth = 0;
0404: pageAreaComposite.setLayout(layout);
0405:
0406: // Build the title area and separator line
0407: Composite titleComposite = new Composite(pageAreaComposite,
0408: SWT.NONE);
0409: layout = new GridLayout();
0410: layout.marginHeight = 0;
0411: layout.marginWidth = 0;
0412: layout.verticalSpacing = 0;
0413: layout.horizontalSpacing = 0;
0414: titleComposite.setLayout(layout);
0415: GridData titleLayoutData = new GridData(
0416: GridData.FILL_HORIZONTAL);
0417: titleLayoutData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
0418: titleComposite.setLayoutData(titleLayoutData);
0419: createTitleArea(titleComposite);
0420:
0421: Label separator = new Label(pageAreaComposite, SWT.HORIZONTAL
0422: | SWT.SEPARATOR);
0423:
0424: separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
0425: | GridData.GRAB_HORIZONTAL));
0426:
0427: // Build the Page container
0428: pageContainer = createPageContainer(pageAreaComposite);
0429: GridData pageContainerData = (GridData) pageContainer
0430: .getLayoutData(); //new GridData(GridData.FILL_BOTH);
0431: pageContainerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
0432: pageContainer.setLayoutData(pageContainerData);
0433: // Build the separator line
0434: Label bottomSeparator = new Label(parent, SWT.HORIZONTAL
0435: | SWT.SEPARATOR);
0436: bottomSeparator.setLayoutData(new GridData(
0437: GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
0438: return composite;
0439: }
0440:
0441: /**
0442: * Create the sash with right control on the right. Note
0443: * that this method assumes GridData for the layout data
0444: * of the rightControl.
0445: * @param composite
0446: * @param rightControl
0447: * @return Sash
0448: *
0449: * @since 3.1
0450: */
0451: protected Sash createSash(final Composite composite,
0452: final Control rightControl) {
0453: final Sash sash = new Sash(composite, SWT.VERTICAL);
0454: sash.setLayoutData(new GridData(GridData.FILL_VERTICAL));
0455: sash.setBackground(composite.getDisplay().getSystemColor(
0456: SWT.COLOR_LIST_BACKGROUND));
0457: // the following listener resizes the tree control based on sash deltas.
0458: // If necessary, it will also grow/shrink the dialog.
0459: sash.addListener(SWT.Selection, new Listener() {
0460: /*
0461: * (non-Javadoc)
0462: *
0463: * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
0464: */
0465: public void handleEvent(Event event) {
0466: if (event.detail == SWT.DRAG)
0467: return;
0468: int shift = event.x - sash.getBounds().x;
0469: GridData data = (GridData) rightControl.getLayoutData();
0470: int newWidthHint = data.widthHint + shift;
0471: if (newWidthHint < 20)
0472: return;
0473: Point computedSize = getShell().computeSize(
0474: SWT.DEFAULT, SWT.DEFAULT);
0475: Point currentSize = getShell().getSize();
0476: // if the dialog wasn't of a custom size we know we can shrink
0477: // it if necessary based on sash movement.
0478: boolean customSize = !computedSize.equals(currentSize);
0479: data.widthHint = newWidthHint;
0480: setLastTreeWidth(newWidthHint);
0481: composite.layout(true);
0482: // recompute based on new widget size
0483: computedSize = getShell().computeSize(SWT.DEFAULT,
0484: SWT.DEFAULT);
0485: // if the dialog was of a custom size then increase it only if
0486: // necessary.
0487: if (customSize)
0488: computedSize.x = Math.max(computedSize.x,
0489: currentSize.x);
0490: computedSize.y = Math
0491: .max(computedSize.y, currentSize.y);
0492: if (computedSize.equals(currentSize))
0493: return;
0494: setShellSize(computedSize.x, computedSize.y);
0495: lastShellSize = getShell().getSize();
0496: }
0497: });
0498: return sash;
0499: }
0500:
0501: /**
0502: * Creates the inner page container.
0503: *
0504: * @param parent
0505: * @return Composite
0506: */
0507: protected Composite createPageContainer(Composite parent) {
0508:
0509: //Create an outer composite for spacing
0510: Composite outer = new Composite(parent, SWT.NONE);
0511:
0512: GridData outerData = new GridData(GridData.FILL_BOTH
0513: | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
0514:
0515: outer.setLayout(new GridLayout());
0516: outer.setLayoutData(outerData);
0517:
0518: Composite result = new Composite(outer, SWT.NONE);
0519:
0520: GridData resultData = new GridData(GridData.FILL_BOTH
0521: | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
0522:
0523: result.setLayout(getPageLayout());
0524: result.setLayoutData(resultData);
0525:
0526: return result;
0527: }
0528:
0529: /**
0530: * Return the layout for the composite that contains
0531: * the pages.
0532: * @return PageLayout
0533: *
0534: * @since 3.1
0535: */
0536: protected Layout getPageLayout() {
0537: return new PageLayout();
0538: }
0539:
0540: /**
0541: * Creates the wizard's title area.
0542: *
0543: * @param parent
0544: * the SWT parent for the title area composite.
0545: * @return the created title area composite.
0546: */
0547: protected Composite createTitleArea(Composite parent) {
0548: // Create the title area which will contain
0549: // a title, message, and image.
0550: int margins = 2;
0551: titleArea = new Composite(parent, SWT.NONE);
0552: FormLayout layout = new FormLayout();
0553: layout.marginHeight = 0;
0554: layout.marginWidth = margins;
0555: titleArea.setLayout(layout);
0556:
0557: GridData layoutData = new GridData(GridData.FILL_HORIZONTAL);
0558: layoutData.verticalAlignment = SWT.TOP;
0559: titleArea.setLayoutData(layoutData);
0560:
0561: // Message label
0562: messageArea = new DialogMessageArea();
0563: messageArea.createContents(titleArea);
0564:
0565: titleArea.addControlListener(new ControlAdapter() {
0566: /* (non-Javadoc)
0567: * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
0568: */
0569: @Override
0570: public void controlResized(ControlEvent e) {
0571: updateMessage();
0572: }
0573: });
0574:
0575: final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
0576: public void propertyChange(PropertyChangeEvent event) {
0577: if (JFaceResources.BANNER_FONT.equals(event
0578: .getProperty()))
0579: updateMessage();
0580: if (JFaceResources.DIALOG_FONT.equals(event
0581: .getProperty())) {
0582: updateMessage();
0583: Font dialogFont = JFaceResources.getDialogFont();
0584: updateTreeFont(dialogFont);
0585: Control[] children = ((Composite) buttonBar)
0586: .getChildren();
0587: for (int i = 0; i < children.length; i++)
0588: children[i].setFont(dialogFont);
0589: }
0590: }
0591: };
0592:
0593: titleArea.addDisposeListener(new DisposeListener() {
0594: public void widgetDisposed(DisposeEvent event) {
0595: JFaceResources.getFontRegistry().removeListener(
0596: fontListener);
0597: }
0598: });
0599: JFaceResources.getFontRegistry().addListener(fontListener);
0600: messageArea.setTitleLayoutData(createMessageAreaData());
0601: messageArea.setMessageLayoutData(createMessageAreaData());
0602: return titleArea;
0603: }
0604:
0605: /**
0606: * Create the layout data for the message area.
0607: *
0608: * @return FormData for the message area.
0609: */
0610: private FormData createMessageAreaData() {
0611: FormData messageData = new FormData();
0612: messageData.top = new FormAttachment(0);
0613: messageData.bottom = new FormAttachment(100);
0614: messageData.right = new FormAttachment(100);
0615: messageData.left = new FormAttachment(0);
0616: return messageData;
0617: }
0618:
0619: /**
0620: * @param parent
0621: * the SWT parent for the tree area controls.
0622: * @return the new <code>Control</code>.
0623: * @since 3.0
0624: */
0625: protected Control createTreeAreaContents(Composite parent) {
0626: // Build the tree an put it into the composite.
0627: treeViewer = createTreeViewer(parent);
0628: treeViewer.setInput(getEditorPageManager());
0629: updateTreeFont(JFaceResources.getDialogFont());
0630: layoutTreeAreaControl(treeViewer.getControl());
0631: return treeViewer.getControl();
0632: }
0633:
0634: /**
0635: * Create a new <code>TreeViewer</code>.
0636: *
0637: * @param parent
0638: * the parent <code>Composite</code>.
0639: * @return the <code>TreeViewer</code>.
0640: * @since 3.0
0641: */
0642: protected TreeViewer createTreeViewer(Composite parent) {
0643: final TreeViewer viewer = new TreeViewer(parent, SWT.NONE);
0644: addListeners(viewer);
0645: viewer.setLabelProvider(new EditorPageLabelProvider());
0646: viewer.setContentProvider(new EditorPageContentProvider());
0647: return viewer;
0648: }
0649:
0650: /**
0651: * Add the listeners to the tree viewer.
0652: * @param viewer
0653: *
0654: * @since 3.1
0655: */
0656: protected void addListeners(final TreeViewer viewer) {
0657: viewer
0658: .addPostSelectionChangedListener(new ISelectionChangedListener() {
0659: private void handleError() {
0660: try {
0661: // remove the listener temporarily so that the events caused
0662: // by the error handling dont further cause error handling
0663: // to occur.
0664: viewer
0665: .removePostSelectionChangedListener(this );
0666: showPageFlippingAbortDialog();
0667: selectCurrentPageAgain();
0668: clearSelectedNode();
0669: } finally {
0670: viewer
0671: .addPostSelectionChangedListener(this );
0672: }
0673: }
0674:
0675: public void selectionChanged(
0676: SelectionChangedEvent event) {
0677: Object selection = getSingleSelection(event
0678: .getSelection());
0679: if (selection instanceof IEditorNode) {
0680: if (!isCurrentPageValid()) {
0681: handleError();
0682: } else if (!showPage((IEditorNode) selection)) {
0683: // Page flipping wasn't successful
0684: handleError();
0685: } else {
0686: // Everything went well
0687: lastSuccessfulNode = (IEditorNode) selection;
0688: }
0689: }
0690: }
0691: });
0692: ((Tree) viewer.getControl())
0693: .addSelectionListener(new SelectionAdapter() {
0694: @Override
0695: public void widgetDefaultSelected(
0696: final SelectionEvent event) {
0697: ISelection selection = viewer.getSelection();
0698: if (selection.isEmpty())
0699: return;
0700: IEditorNode singleSelection = getSingleSelection(selection);
0701: boolean expanded = viewer
0702: .getExpandedState(singleSelection);
0703: viewer.setExpandedState(singleSelection,
0704: !expanded);
0705: }
0706: });
0707: //Register help listener on the tree to use context sensitive help
0708: viewer.getControl().addHelpListener(new HelpListener() {
0709: public void helpRequested(HelpEvent event) {
0710: // call perform help on the current page
0711: if (currentPage != null) {
0712: currentPage.performHelp();
0713: }
0714: }
0715: });
0716: }
0717:
0718: /**
0719: * Find the <code>IEditorNode</code> that has data the same id as the
0720: * supplied value.
0721: *
0722: * @param nodeId
0723: * the id to search for.
0724: * @return <code>IEditorNode</code> or <code>null</code> if not
0725: * found.
0726: */
0727: protected IEditorNode findNodeMatching(String nodeId) {
0728: List nodes = editorPageManager
0729: .getElements(EditorPageManager.POST_ORDER);
0730: for (Iterator i = nodes.iterator(); i.hasNext();) {
0731: IEditorNode node = (IEditorNode) i.next();
0732: if (node.getId().equalsIgnoreCase(nodeId))
0733: return node;
0734: }
0735: return null;
0736: }
0737:
0738: /**
0739: * Get the last known right side width.
0740: *
0741: * @return the width.
0742: */
0743: protected int getLastRightWidth() {
0744: return lastTreeWidth;
0745: }
0746:
0747: /**
0748: * Returns the preference mananger used by this preference dialog.
0749: *
0750: * @return the preference mananger
0751: */
0752: public EditorPageManager getEditorPageManager() {
0753: return editorPageManager;
0754: }
0755:
0756: /**
0757: * Get the name of the selected item preference
0758: *
0759: * @return String
0760: */
0761: protected String getSelectedNode() {
0762: return lastPageId;
0763: }
0764:
0765: /**
0766: * @param selection
0767: * the <code>ISelection</code> to examine.
0768: * @return the first element, or null if empty.
0769: */
0770: protected IEditorNode getSingleSelection(ISelection selection) {
0771: if (!selection.isEmpty()) {
0772: IStructuredSelection structured = (IStructuredSelection) selection;
0773: if (structured.getFirstElement() instanceof IEditorNode)
0774: return (IEditorNode) structured.getFirstElement();
0775: }
0776: return null;
0777: }
0778:
0779: /**
0780: * @return the <code>TreeViewer</code> for this dialog.
0781: * @since 3.0
0782: */
0783: protected TreeViewer getTreeViewer() {
0784: return treeViewer;
0785: }
0786:
0787: /**
0788: * Notifies that the window's close button was pressed, the close menu was
0789: * selected, or the ESCAPE key pressed.
0790: * <p>
0791: * The default implementation of this framework method sets the window's
0792: * return code to <code>CANCEL</code> and closes the window using
0793: * <code>close</code>. Subclasses may extend or reimplement.
0794: * </p>
0795: */
0796: @Override
0797: protected void handleShellCloseEvent() {
0798: // handle the same as pressing cancel
0799: cancelPressed();
0800: }
0801:
0802: /**
0803: * Notifies of the pressing of the Help button.
0804: * <p>
0805: * The default implementation of this framework method calls
0806: * <code>performHelp</code> on the currently active page.
0807: * </p>
0808: */
0809: protected void helpPressed() {
0810: if (currentPage != null) {
0811: currentPage.performHelp();
0812: }
0813: }
0814:
0815: /**
0816: * Returns whether the current page is valid.
0817: *
0818: * @return <code>false</code> if the current page is not valid, or or
0819: * <code>true</code> if the current page is valid or there is no
0820: * current page
0821: */
0822: protected boolean isCurrentPageValid() {
0823: if (currentPage == null)
0824: return true;
0825: return currentPage.isValid();
0826: }
0827:
0828: /**
0829: * @param control
0830: * the <code>Control</code> to lay out.
0831: * @since 3.0
0832: */
0833: protected void layoutTreeAreaControl(Control control) {
0834: GridData gd = new GridData(GridData.FILL_VERTICAL);
0835: gd.widthHint = getLastRightWidth();
0836: gd.verticalSpan = 1;
0837: control.setLayoutData(gd);
0838: }
0839:
0840: /**
0841: * The preference dialog implementation of this <code>Dialog</code>
0842: * framework method sends <code>performOk</code> to all pages of the
0843: * preference dialog, then calls <code>handleSave</code> on this dialog to
0844: * save any state, and then calls <code>close</code> to close this dialog.
0845: */
0846: @Override
0847: protected void okPressed() {
0848: SafeRunnable.run(new SafeRunnable() {
0849:
0850: /*
0851: * (non-Javadoc)
0852: *
0853: * @see org.eclipse.core.runtime.ISafeRunnable#run()
0854: */
0855: public void run() {
0856: getButton(IDialogConstants.OK_ID).setEnabled(false);
0857: boolean hasFailedOK = false;
0858: try {
0859: // Notify all the pages and give them a chance to abort
0860: Iterator nodes = editorPageManager.getElements(
0861: EditorPageManager.PRE_ORDER).iterator();
0862: while (nodes.hasNext()) {
0863: IEditorNode node = (IEditorNode) nodes.next();
0864: IEditorPage page = node.getPage();
0865: if (page != null) {
0866: if (!page.performOk()) {
0867: hasFailedOK = true;
0868: return;
0869: }
0870: }
0871: }
0872: } catch (Exception e) {
0873: handleException(e);
0874: } finally {
0875: //Don't bother closing if the OK failed
0876: if (hasFailedOK)
0877: return;
0878:
0879: close();
0880: }
0881: }
0882:
0883: /*
0884: * (non-Javadoc)
0885: *
0886: * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable)
0887: */
0888: @Override
0889: public void handleException(Throwable e) {
0890: SLDPlugin.getDefault().getLog().log(
0891: new Status(IStatus.ERROR, SLDPlugin.ID, 0, e
0892: .toString(), e));
0893:
0894: clearSelectedNode();
0895: String message = JFaceResources
0896: .getString("SafeRunnable.errorMessage"); //$NON-NLS-1$
0897: MessageDialog.openError(getShell(), JFaceResources
0898: .getString("Error"), message); //$NON-NLS-1$
0899:
0900: }
0901: });
0902: }
0903:
0904: /**
0905: * Selects the page determined by <code>lastSuccessfulNode</code> in the
0906: * page hierarchy.
0907: */
0908: void selectCurrentPageAgain() {
0909: if (lastSuccessfulNode == null)
0910: return;
0911: getTreeViewer().setSelection(
0912: new StructuredSelection(lastSuccessfulNode));
0913: currentPage.setVisible(true);
0914: }
0915:
0916: /**
0917: * Selects the saved item in the tree of preference pages. If it cannot do
0918: * this it saves the first one.
0919: */
0920: protected void selectSavedItem() {
0921: IEditorNode node = findNodeMatching(getSelectedNode());
0922: if (node == null) {
0923: IEditorNode[] nodes = editorPageManager.getRootSubNodes();
0924: if (lastPageId != null)
0925: node = findNodeMatching(lastPageId);
0926: if (node == null && nodes.length > 0)
0927: node = nodes[0];
0928: }
0929: if (node != null) {
0930: getTreeViewer().setSelection(new StructuredSelection(node),
0931: true);
0932: // Keep focus in tree. See bugs 2692, 2621, and 6775.
0933: getTreeViewer().getControl().setFocus();
0934: }
0935: }
0936:
0937: /**
0938: * Display the given error message. The currently displayed message is saved
0939: * and will be redisplayed when the error message is set to
0940: * <code>null</code>.
0941: *
0942: * @param newErrorMessage
0943: * the errorMessage to display or <code>null</code>
0944: */
0945: public void setErrorMessage(String newErrorMessage) {
0946: if (newErrorMessage == null)
0947: messageArea.clearErrorMessage();
0948: else
0949: messageArea.updateText(newErrorMessage,
0950: IMessageProvider.ERROR);
0951: }
0952:
0953: /**
0954: * Save the last known tree width.
0955: *
0956: * @param width
0957: * the width.
0958: */
0959: private void setLastTreeWidth(int width) {
0960: lastTreeWidth = width;
0961: }
0962:
0963: /**
0964: * Sets whether a Help button is available for this dialog.
0965: * <p>
0966: * Clients must call this framework method before the dialog's control has
0967: * been created.
0968: * <p>
0969: *
0970: * @param b
0971: * <code>true</code> to include a Help button, and
0972: * <code>false</code> to not include one (the default)
0973: */
0974: public void setHelpAvailable(boolean b) {
0975: isHelpAvailable = b;
0976: }
0977:
0978: /**
0979: * Set the message text. If the message line currently displays an error,
0980: * the message is stored and will be shown after a call to clearErrorMessage
0981: * <p>
0982: * Shortcut for <code>setMessage(newMessage, NONE)</code>
0983: * </p>
0984: *
0985: * @param newMessage
0986: * the message, or <code>null</code> to clear the message
0987: */
0988: public void setMessage(String newMessage) {
0989: setMessage(newMessage, IMessageProvider.NONE);
0990: }
0991:
0992: /**
0993: * Sets the message for this dialog with an indication of what type of
0994: * message it is.
0995: * <p>
0996: * The valid message types are one of <code>NONE</code>,
0997: * <code>INFORMATION</code>,<code>WARNING</code>, or
0998: * <code>ERROR</code>.
0999: * </p>
1000: * <p>
1001: * Note that for backward compatibility, a message of type
1002: * <code>ERROR</code> is different than an error message (set using
1003: * <code>setErrorMessage</code>). An error message overrides the current
1004: * message until the error message is cleared. This method replaces the
1005: * current message and does not affect the error message.
1006: * </p>
1007: *
1008: * @param newMessage
1009: * the message, or <code>null</code> to clear the message
1010: * @param newType
1011: * the message type
1012: * @since 2.0
1013: */
1014: public void setMessage(String newMessage, int newType) {
1015: messageArea.updateText(newMessage, newType);
1016: }
1017:
1018: /**
1019: * Sets the minimum page size.
1020: *
1021: * @param minWidth
1022: * the minimum page width
1023: * @param minHeight
1024: * the minimum page height
1025: * @see #setMinimumPageSize(Point)
1026: */
1027: public void setMinimumPageSize(int minWidth, int minHeight) {
1028: minimumPageSize.x = minWidth;
1029: minimumPageSize.y = minHeight;
1030: }
1031:
1032: /**
1033: * Sets the minimum page size.
1034: *
1035: * @param size
1036: * the page size encoded as <code>new Point(width,height)</code>
1037: * @see #setMinimumPageSize(int,int)
1038: */
1039: public void setMinimumPageSize(Point size) {
1040: minimumPageSize.x = size.x;
1041: minimumPageSize.y = size.y;
1042: }
1043:
1044: /**
1045: * Save the currently selected node.
1046: */
1047: private void setSelectedNode() {
1048: String storeValue = null;
1049: IStructuredSelection selection = (IStructuredSelection) getTreeViewer()
1050: .getSelection();
1051: if (selection.size() == 1) {
1052: IEditorNode node = (IEditorNode) selection
1053: .getFirstElement();
1054: storeValue = node.getId();
1055: }
1056: setSelectedNode(storeValue);
1057: }
1058:
1059: /**
1060: * Sets the name of the selected item preference.
1061: *
1062: * @param pageId
1063: * The identifier for the page
1064: */
1065: public void setSelectedNode(String pageId) {
1066: lastPageId = pageId;
1067: }
1068:
1069: /**
1070: * Changes the shell size to the given size, ensuring that it is no larger
1071: * than the display bounds.
1072: *
1073: * @param width
1074: * the shell width
1075: * @param height
1076: * the shell height
1077: */
1078: private void setShellSize(int width, int height) {
1079: Rectangle preferred = getShell().getBounds();
1080: preferred.width = width;
1081: preferred.height = height;
1082: getShell().setBounds(getConstrainedShellBounds(preferred));
1083: }
1084:
1085: /**
1086: * Shows the preference page corresponding to the given preference node.
1087: * Does nothing if that page is already current.
1088: *
1089: * @param node
1090: * the preference node, or <code>null</code> if none
1091: * @return <code>true</code> if the page flip was successful, and
1092: * <code>false</code> is unsuccessful
1093: */
1094: protected boolean showPage(IEditorNode node) {
1095: if (node == null)
1096: return false;
1097: // Create the page if nessessary
1098: if (node.getPage() == null)
1099: createPage(node);
1100: if (node.getPage() == null)
1101: return false;
1102: IEditorPage newPage = getPage(node);
1103: if (newPage == currentPage)
1104: return true;
1105: if (currentPage != null) {
1106: if (!currentPage.okToLeave())
1107: return false;
1108: }
1109: IEditorPage oldPage = currentPage;
1110: currentPage = newPage;
1111: // Set the new page's container
1112: currentPage.setContainer(this );
1113: // Ensure that the page control has been created
1114: // (this allows lazy page control creation)
1115: if (currentPage.getControl() == null) {
1116: final boolean[] failed = { false };
1117: SafeRunnable.run(new ISafeRunnable() {
1118: public void handleException(Throwable e) {
1119: failed[0] = true;
1120: }
1121:
1122: public void run() {
1123: createPageControl(currentPage, pageContainer);
1124: }
1125: });
1126: if (failed[0])
1127: return false;
1128: // the page is responsible for ensuring the created control is
1129: // accessable
1130: // via getControl.
1131: Assert.isNotNull(currentPage.getControl());
1132: }
1133: // Force calculation of the page's description label because
1134: // label can be wrapped.
1135: final Point[] size = new Point[1];
1136: final Point failed = new Point(-1, -1);
1137: SafeRunnable.run(new ISafeRunnable() {
1138: public void handleException(Throwable e) {
1139: size[0] = failed;
1140: }
1141:
1142: public void run() {
1143: size[0] = currentPage.computeSize();
1144: }
1145: });
1146: if (size[0].equals(failed))
1147: return false;
1148: Point contentSize = size[0];
1149: // Do we need resizing. Computation not needed if the
1150: // first page is inserted since computing the dialog's
1151: // size is done by calling dialog.open().
1152: // Also prevent auto resize if the user has manually resized
1153: Shell shell = getShell();
1154: Point shellSize = shell.getSize();
1155: if (oldPage != null) {
1156: Rectangle rect = pageContainer.getClientArea();
1157: Point containerSize = new Point(rect.width, rect.height);
1158: int hdiff = contentSize.x - containerSize.x;
1159: int vdiff = contentSize.y - containerSize.y;
1160: if ((hdiff > 0 || vdiff > 0)
1161: && shellSize.equals(lastShellSize)) {
1162: hdiff = Math.max(0, hdiff);
1163: vdiff = Math.max(0, vdiff);
1164: setShellSize(shellSize.x + hdiff, shellSize.y + vdiff);
1165: lastShellSize = shell.getSize();
1166: if (currentPage.getControl().getSize().x == 0)
1167: currentPage.getControl().setSize(containerSize);
1168:
1169: } else
1170: //Set the size to be sure we use the result of computeSize
1171: currentPage.setSize(containerSize);
1172: }
1173: // Ensure that all other pages are invisible
1174: // (including ones that triggered an exception during
1175: // their creation).
1176: Control[] children = pageContainer.getChildren();
1177: Control currentControl = currentPage.getControl();
1178: for (int i = 0; i < children.length; i++) {
1179: if (children[i] != currentControl)
1180: children[i].setVisible(false);
1181: }
1182: // Make the new page visible
1183: currentPage.setVisible(true);
1184: if (oldPage != null)
1185: oldPage.setVisible(false);
1186: // update the dialog controls
1187: update();
1188: return true;
1189: }
1190:
1191: /**
1192: * Create the page for the node.
1193: * @param node
1194: *
1195: * @since 3.1
1196: */
1197: protected void createPage(IEditorNode node) {
1198: node.createPage(getPageContainer(), this );
1199: }
1200:
1201: /**
1202: * Get the page for the node.
1203: * @param node
1204: * @return IEditorPage
1205: *
1206: * @since 3.1
1207: */
1208: protected IEditorPage getPage(IEditorNode node) {
1209: return node.getPage();
1210: }
1211:
1212: /**
1213: * Shows the "Page Flipping abort" dialog.
1214: */
1215: void showPageFlippingAbortDialog() {
1216: // MessageDialog.openError(getShell(), JFaceResources
1217: // .getString("AbortPageFlippingDialog.title"), //$NON-NLS-1$
1218: // JFaceResources.getString("AbortPageFlippingDialog.message")); //$NON-NLS-1$
1219: }
1220:
1221: /**
1222: * Updates this dialog's controls to reflect the current page.
1223: */
1224: protected void update() {
1225: // Update the title bar
1226: updateTitle();
1227: // Update the message line
1228: updateMessage();
1229: // Update the buttons
1230: updateButtons();
1231: //Saved the selected node in the preferences
1232: setSelectedNode();
1233: firePageChanged(new PageChangedEvent(this , getCurrentPage()));
1234: }
1235:
1236: /*
1237: * (non-Javadoc)
1238: *
1239: * @see org.eclipse.jface.preference.IEditorPageContainer#updateButtons()
1240: */
1241: public void updateButtons() {
1242: okButton.setEnabled(isCurrentPageValid());
1243: }
1244:
1245: /*
1246: * (non-Javadoc)
1247: *
1248: * @see org.eclipse.jface.preference.IEditorPageContainer#updateMessage()
1249: */
1250: public void updateMessage() {
1251: String message = null;
1252: String errorMessage = null;
1253: if (currentPage != null) {
1254: message = currentPage.getMessage();
1255: errorMessage = currentPage.getErrorMessage();
1256: }
1257: int messageType = IMessageProvider.NONE;
1258: if (message != null && currentPage instanceof IMessageProvider)
1259: messageType = ((IMessageProvider) currentPage)
1260: .getMessageType();
1261:
1262: if (errorMessage == null) {
1263: if (showingError) {
1264: // we were previously showing an error
1265: showingError = false;
1266: }
1267: } else {
1268: message = errorMessage;
1269: messageType = IMessageProvider.ERROR;
1270: if (!showingError) {
1271: // we were not previously showing an error
1272: showingError = true;
1273: }
1274: }
1275: messageArea.updateText(message, messageType);
1276: }
1277:
1278: /*
1279: * (non-Javadoc)
1280: *
1281: * @see org.eclipse.jface.preference.IEditorPageContainer#updateTitle()
1282: */
1283: public void updateTitle() {
1284: if (currentPage == null)
1285: return;
1286: messageArea.showTitle(currentPage.getTitle(), currentPage
1287: .getImage());
1288: }
1289:
1290: /**
1291: * Update the tree to use the specified <code>Font</code>.
1292: *
1293: * @param dialogFont
1294: * the <code>Font</code> to use.
1295: * @since 3.0
1296: */
1297: protected void updateTreeFont(Font dialogFont) {
1298: getTreeViewer().getControl().setFont(dialogFont);
1299: }
1300:
1301: /**
1302: * Returns the currentPage.
1303: * @return IEditorPage
1304: * @since 3.1
1305: */
1306: protected IEditorPage getCurrentPage() {
1307: return currentPage;
1308: }
1309:
1310: /**
1311: * Sets the current page.
1312: * @param currentPage
1313: *
1314: * @since 3.1
1315: */
1316: protected void setCurrentPage(IEditorPage currentPage) {
1317: this .currentPage = currentPage;
1318: }
1319:
1320: /**
1321: * Set the treeViewer.
1322: * @param treeViewer
1323: *
1324: * @since 3.1
1325: */
1326: protected void setTreeViewer(TreeViewer treeViewer) {
1327: this .treeViewer = treeViewer;
1328: }
1329:
1330: /**
1331: * Get the composite that is showing the page.
1332: *
1333: * @return Composite.
1334: *
1335: * @since 3.1
1336: */
1337: protected Composite getPageContainer() {
1338: return this .pageContainer;
1339: }
1340:
1341: /**
1342: * Set the composite that is showing the page.
1343: * @param pageContainer Composite
1344: *
1345: * @since 3.1
1346: */
1347: protected void setPageContainer(Composite pageContainer) {
1348: this .pageContainer = pageContainer;
1349: }
1350:
1351: /**
1352: * Create the page control for the supplied page.
1353: *
1354: * @param page - the preference page to be shown
1355: * @param parent - the composite to parent the page
1356: *
1357: * @since 3.1
1358: */
1359: protected void createPageControl(IEditorPage page, Composite parent) {
1360: page.createControl(parent);
1361: }
1362:
1363: /**
1364: * @see org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage()
1365: *
1366: * @since 3.1
1367: */
1368: public Object getSelectedPage() {
1369: return getCurrentPage();
1370: }
1371:
1372: /**
1373: * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
1374: * @since 3.1
1375: */
1376: public void addPageChangedListener(IPageChangedListener listener) {
1377: pageChangedListeners.add(listener);
1378: }
1379:
1380: /**
1381: * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
1382: * @since 3.1
1383: */
1384: public void removePageChangedListener(IPageChangedListener listener) {
1385: pageChangedListeners.remove(listener);
1386:
1387: }
1388:
1389: /**
1390: * Notifies any selection changed listeners that the selected page
1391: * has changed.
1392: * Only listeners registered at the time this method is called are notified.
1393: *
1394: * @param event a selection changed event
1395: *
1396: * @see IPageChangedListener#pageChanged
1397: *
1398: * @since 3.1
1399: */
1400: protected void firePageChanged(final PageChangedEvent event) {
1401: Object[] listeners = pageChangedListeners.getListeners();
1402: for (int i = 0; i < listeners.length; i++) {
1403: final IPageChangedListener l = (IPageChangedListener) listeners[i];
1404: SafeRunnable.run(new SafeRunnable() {
1405: public void run() {
1406: l.pageChanged(event);
1407: }
1408: });
1409: }
1410: }
1411: }
|