| /******************************************************************************* |
| * Copyright (c) 2000, 2010 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.gef.ui.palette.customize; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.swt.custom.CLabel; |
| import org.eclipse.swt.custom.StackLayout; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.FillLayout; |
| 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.ControlPaintHandler; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.ToolItem; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.swt.widgets.Widget; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.ActionContributionItem; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuCreator; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceColors; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.part.PageBook; |
| |
| import org.eclipse.draw2d.ColorConstants; |
| import org.eclipse.draw2d.rap.swt.SWT; |
| import org.eclipse.draw2d.widgets.MultiLineLabel; |
| |
| import org.eclipse.gef.internal.Internal; |
| import org.eclipse.gef.internal.ui.palette.ToolbarDropdownContributionItem; |
| import org.eclipse.gef.palette.PaletteEntry; |
| import org.eclipse.gef.palette.PaletteRoot; |
| import org.eclipse.gef.ui.palette.PaletteCustomizer; |
| import org.eclipse.gef.ui.palette.PaletteMessages; |
| |
| /** |
| * This class implements a default dialog that allows customization of the |
| * different entries/items on a GEF palette, i.e. the model behind the palette. |
| * <p> |
| * The construction of the dialog is broken down into different methods in order |
| * to allow clients to further customize the appearance of the dialog, if so |
| * desired. |
| * </p> |
| * <p> |
| * This dialog can be re-used, i.e., it can be re-opened once closed. There is |
| * no need to create a new <code>PaletteCustomizerDialog</code> everytime a |
| * palette needs to be customized. |
| * </p> |
| * |
| * @author Pratik Shah |
| * @see org.eclipse.gef.palette.PaletteEntry |
| * @see org.eclipse.gef.ui.palette.PaletteCustomizer |
| */ |
| public class PaletteCustomizerDialog extends Dialog implements |
| EntryPageContainer { |
| |
| /** |
| * The unique ID for the Apply Button. It can be used to retrieve that |
| * widget from the internal map (using {@link #getWidget(int)} or |
| * {@link #getButton(int)}), or to identify that widget in |
| * {@link #buttonPressed(int)}. |
| */ |
| protected static final int APPLY_ID = IDialogConstants.CLIENT_ID + 1; |
| |
| /** |
| * Sub-classes that need to create their own unique IDs should do so by |
| * adding to this ID. |
| */ |
| protected static final int CLIENT_ID = 16; |
| |
| private HashMap widgets = new HashMap(); |
| private HashMap entriesToPages = new HashMap(); |
| private List actions; |
| |
| private String errorMessage; |
| private Tree tree; |
| private Composite titlePage, errorPage; |
| private PageBook propertiesPanelContainer; |
| // This PageBook is used to switch the title of the properties panel to |
| // either an error |
| // message or the currently active entry's label |
| private PageBook titleSwitcher; |
| private PaletteCustomizer customizer; |
| private EntryPage activePage, noSelectionPage; |
| private CLabel title; |
| private MultiLineLabel errorTitle; |
| private Image titleImage; |
| private TreeViewer treeviewer; |
| private ILabelProvider treeViewerLabelProvider; |
| private PaletteEntry activeEntry; |
| private PaletteEntry initialSelection; |
| private PaletteRoot root; |
| private PropertyChangeListener titleUpdater = new PropertyChangeListener() { |
| public void propertyChange(PropertyChangeEvent evt) { |
| if (title == null) { |
| return; |
| } |
| |
| title.setText(((PaletteEntry) evt.getSource()).getLabel()); |
| } |
| }; |
| private ISelectionChangedListener pageFlippingPreventer = new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| treeviewer.removePostSelectionChangedListener(this); |
| treeviewer.setSelection(new StructuredSelection(activeEntry)); |
| } |
| }; |
| private boolean isSetup = true; |
| |
| /** |
| * Constructs a new customizer dialog. |
| * |
| * @param shell |
| * the parent Shell |
| * @param customizer |
| * the customizer |
| * @param root |
| * the palette root |
| */ |
| public PaletteCustomizerDialog(Shell shell, PaletteCustomizer customizer, |
| PaletteRoot root) { |
| super(shell); |
| this.customizer = customizer; |
| this.root = root; |
| setShellStyle(getShellStyle() | SWT.RESIZE); |
| } |
| |
| /** |
| * This method will be invoked whenever any <code>Button</code> created |
| * using {@link #createButton(Composite, int, String, int, ImageDescriptor)} |
| * or {@link Dialog#createButton(Composite, int, String, boolean)} is |
| * selected. |
| * |
| * @see Dialog#buttonPressed(int) |
| */ |
| protected void buttonPressed(int buttonId) { |
| if (APPLY_ID == buttonId) { |
| handleApplyPressed(); |
| } else { |
| super.buttonPressed(buttonId); |
| } |
| } |
| |
| /** |
| * This method should be invoked by EntryPages when an error that they had |
| * earlier reported (using {@link #showProblem(String)}) is fixed. This will |
| * hide the error message, enable the OK and Apply buttons and re-allow |
| * changing selection in the outline tree. |
| * |
| * @see org.eclipse.gef.ui.palette.customize.EntryPageContainer#clearProblem() |
| * @see #showProblem(String) |
| */ |
| public void clearProblem() { |
| if (errorMessage != null) { |
| titleSwitcher.showPage(titlePage); |
| getButton(IDialogConstants.OK_ID).setEnabled(true); |
| getButton(APPLY_ID).setEnabled(true); |
| errorMessage = null; |
| } |
| } |
| |
| /** |
| * <p> |
| * NOTE: This dialog can be re-opened. |
| * </p> |
| * |
| * @see org.eclipse.jface.window.Window#close() |
| */ |
| public boolean close() { |
| // Remove listeners |
| if (activeEntry != null) { |
| activeEntry.removePropertyChangeListener(titleUpdater); |
| } |
| |
| // Save or dump changes |
| // This needs to be done here and not in the handle methods because the |
| // user can |
| // also close the dialog with the 'X' in the top right of the window |
| // (which |
| // corresponds to a cancel). |
| if (getReturnCode() == OK) { |
| save(); |
| } else { |
| revertToSaved(); |
| } |
| |
| // Close the dialog |
| boolean returnVal = super.close(); |
| |
| // Reset variables |
| entriesToPages.clear(); |
| widgets.clear(); |
| actions = null; |
| activePage = null; |
| tree = null; |
| propertiesPanelContainer = null; |
| titleSwitcher = null; |
| titlePage = null; |
| errorPage = null; |
| title = null; |
| errorTitle = null; |
| treeviewer = null; |
| noSelectionPage = null; |
| initialSelection = null; |
| activeEntry = null; |
| errorMessage = null; |
| isSetup = true; |
| |
| return returnVal; |
| } |
| |
| /** |
| * @see org.eclipse.jface.window.Window#configureShell(Shell) |
| */ |
| protected void configureShell(Shell newShell) { |
| newShell.setText(PaletteMessages.CUSTOMIZE_DIALOG_TITLE); |
| super.configureShell(newShell); |
| } |
| |
| /** |
| * This method should not be used to create buttons for the button bar. Use |
| * {@link Dialog#createButton(Composite, int, String, boolean)} for that. |
| * This method can be used to create any other button in the dialog. The |
| * parent <code>Composite</code> must have a GridLayout. These buttons will |
| * be available through {@link #getButton(int)} and {@link #getWidget(int)}. |
| * Ensure that the various buttons created by this method are given unique |
| * IDs. Pass in a <code>null</code> image descriptor if you don't want the |
| * button to have an icon. This method will take care of disposing the |
| * images that it creates. {@link #buttonPressed(int)} will be called when |
| * any of the buttons created by this method are clicked (selected). |
| * |
| * @param parent |
| * The composite in which the button is to be created |
| * @param id |
| * The button's unique ID |
| * @param label |
| * The button's text |
| * @param stylebits |
| * The style bits for creating the button (eg., |
| * <code>SWT.PUSH</code> or <code>SWT.CHECK</code>) |
| * @param descriptor |
| * The ImageDescriptor from which the image/icon for this button |
| * should be created |
| * @return The newly created button for convenience |
| */ |
| protected Button createButton(Composite parent, int id, String label, |
| int stylebits, ImageDescriptor descriptor) { |
| Button button = new Button(parent, stylebits); |
| button.setText(label); |
| button.setFont(parent.getFont()); |
| GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); |
| button.setLayoutData(data); |
| |
| button.setData(new Integer(id)); |
| button.addSelectionListener(new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent event) { |
| buttonPressed(((Integer) event.widget.getData()).intValue()); |
| } |
| }); |
| widgets.put(new Integer(id), button); |
| |
| if (descriptor != null) { |
| button.setImage(new Image(parent.getDisplay(), descriptor |
| .getImageData())); |
| button.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| Image img = ((Button) e.getSource()).getImage(); |
| if (img != null && !img.isDisposed()) { |
| img.dispose(); |
| } |
| } |
| }); |
| } |
| |
| return button; |
| } |
| |
| /** |
| * Creates the OK, Cancel and Apply buttons |
| * |
| * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(Composite) |
| */ |
| protected void createButtonsForButtonBar(Composite parent) { |
| super.createButtonsForButtonBar(parent); |
| createButton(parent, APPLY_ID, PaletteMessages.APPLY_LABEL, false); |
| } |
| |
| /** |
| * The dialog area contains the following: |
| * <UL> |
| * <LI>Outline ({@link #createOutline(Composite)})</LI> |
| * <LI>Properties Panel ({@link #createPropertiesPanel(Composite)})</LI> |
| * </UL> |
| * |
| * <p> |
| * It is recommended that this method not be overridden. Override one of the |
| * methods that this method calls in order to customize the appearance of |
| * the dialog. |
| * </p> |
| * |
| * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(Composite) |
| */ |
| protected Control createDialogArea(Composite parent) { |
| Composite composite = (Composite) super.createDialogArea(parent); |
| GridLayout gridLayout = (GridLayout) composite.getLayout(); |
| gridLayout.numColumns = 2; |
| gridLayout.horizontalSpacing = 10; |
| |
| // Create the tree |
| Control child = createOutline(composite); |
| GridData data = new GridData(GridData.VERTICAL_ALIGN_FILL); |
| data.verticalSpan = 2; |
| child.setLayoutData(data); |
| |
| // Create the panel where the properties of the selected palette entry |
| // will |
| // be shown |
| child = createPropertiesPanel(composite); |
| child.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| // Create the separator b/w the dialog area and the button bar |
| Label label = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); |
| data = new GridData(GridData.FILL_HORIZONTAL); |
| data.horizontalSpan = 2; |
| label.setLayoutData(data); |
| |
| // Select an element in the outline and set focus on the outline. |
| if (initialSelection == null) { |
| // We have to manually select the first item in the tree, because |
| // otherwise the |
| // will scroll to show the last item, and then will select the first |
| // visible item. |
| List children = getPaletteRoot().getChildren(); |
| if (!children.isEmpty()) { |
| initialSelection = (PaletteEntry) children.get(0); |
| } |
| } |
| if (initialSelection != null) { |
| treeviewer.setSelection(new StructuredSelection(initialSelection)); |
| } else { |
| setActiveEntry(null); |
| } |
| isSetup = false; |
| tree.setFocus(); |
| |
| return composite; |
| } |
| |
| /** |
| * Creates the outline part of the dialog. |
| * |
| * <p> |
| * The outline creates the following: |
| * <UL> |
| * <LI>ToolBar ({@link #createOutlineToolBar(Composite)})</LI> |
| * <LI>TreeViewer ({@link #createOutlineTreeViewer(Composite)})</LI> |
| * <LI>Context menu ({@link #createOutlineContextMenu()})</LI> |
| * </UL> |
| * </p> |
| * |
| * @param container |
| * The Composite within which the outline has to be created |
| * @return The newly created Control that has the outline |
| */ |
| protected Control createOutline(Composite container) { |
| // Create the Composite that will contain the outline |
| Composite composite = new Composite(container, SWT.NONE); |
| composite.setFont(container.getFont()); |
| GridLayout layout = new GridLayout(); |
| layout.horizontalSpacing = 0; |
| layout.verticalSpacing = 0; |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| composite.setLayout(layout); |
| |
| // Create the ToolBar |
| createOutlineToolBar(composite); |
| |
| // Create the actual outline (TreeViewer) |
| treeviewer = createOutlineTreeViewer(composite); |
| tree = treeviewer.getTree(); |
| |
| // Create the context menu for the Tree |
| tree.setMenu(createOutlineContextMenu()); |
| |
| return composite; |
| } |
| |
| /** |
| * Creates the actions that manipulate the palette model. These actions will |
| * populate the toolbar and the outline's context menu. |
| * |
| * <p> |
| * IMPORTANT: All the elements in the returned List MUST be |
| * <code>PaletteCustomizationAction</code>s. |
| * </p> |
| * |
| * @return A List of {@link PaletteCustomizationAction |
| * PaletteCustomizationActions} |
| */ |
| protected List createOutlineActions() { |
| List actions = new ArrayList(); |
| actions.add(new NewAction()); |
| actions.add(new DeleteAction()); |
| actions.add(new MoveDownAction()); |
| actions.add(new MoveUpAction()); |
| return actions; |
| } |
| |
| /** |
| * Uses a <code>MenuManager</code> to create the context menu for the |
| * outline. The <code>IActions</code> used to create the context menu are |
| * those created in {@link #createOutlineActions()}. |
| * |
| * @return The newly created Menu |
| */ |
| protected Menu createOutlineContextMenu() { |
| // MenuManager for the tree's context menu |
| final MenuManager outlineMenu = new MenuManager(); |
| |
| List actions = getOutlineActions(); |
| // Add all the actions to the context menu |
| for (Iterator iter = actions.iterator(); iter.hasNext();) { |
| IAction action = (IAction) iter.next(); |
| if (action instanceof IMenuCreator) |
| outlineMenu.add(new ActionContributionItem(action) { |
| public boolean isDynamic() { |
| return true; |
| } |
| }); |
| else |
| outlineMenu.add(action); |
| // Add separators after new and delete |
| if (action instanceof NewAction || action instanceof DeleteAction) { |
| outlineMenu.add(new Separator()); |
| } |
| } |
| |
| outlineMenu.addMenuListener(new IMenuListener() { |
| public void menuAboutToShow(IMenuManager manager) { |
| outlineMenu.update(true); |
| } |
| }); |
| |
| outlineMenu.createContextMenu(tree); |
| return outlineMenu.getMenu(); |
| } |
| |
| /** |
| * Uses a ToolBarManager to create the ToolBar in the outline part of the |
| * dialog. The Actions used in the ToolBarManager are those that are created |
| * in {@link #createOutlineActions()}. |
| * |
| * @param parent |
| * The Composite to which the ToolBar is to be added |
| * @return The newly created ToolBar |
| */ |
| protected Control createOutlineToolBar(Composite parent) { |
| // A customized composite for the toolbar |
| final Composite composite = new Composite(parent, SWT.NONE) { |
| public Rectangle getClientArea() { |
| Rectangle area = super.getClientArea(); |
| area.x += 2; |
| area.y += 2; |
| area.height -= 2; |
| area.width -= 4; |
| return area; |
| } |
| |
| public Point computeSize(int wHint, int hHint, boolean changed) { |
| Point size = super.computeSize(wHint, hHint, changed); |
| size.x += 4; |
| size.y += 2; |
| return size; |
| } |
| }; |
| composite.setFont(parent.getFont()); |
| composite.setLayout(new FillLayout()); |
| |
| // A paint listener that draws an etched border around the toolbar |
| ControlPaintHandler helper = new ControlPaintHandler(composite); |
| helper.addPaintListener(new PaintListener() { |
| public void paintControl(PaintEvent e) { |
| Rectangle area = composite.getBounds(); |
| GC gc = e.gc; |
| // UNSUPPORTED - api not implemented in RAP |
| // gc.setLineStyle(SWT.LINE_SOLID); |
| gc.setForeground(ColorConstants.buttonDarker); |
| gc.drawLine(area.x, area.y, area.x + area.width - 2, area.y); |
| gc.drawLine(area.x, area.y, area.x, area.y + area.height - 1); |
| gc.drawLine(area.x + area.width - 2, area.y, area.x |
| + area.width - 2, area.y + area.height - 1); |
| gc.setForeground(ColorConstants.buttonLightest); |
| gc.drawLine(area.x + 1, area.y + 1, area.x + area.width - 3, |
| area.y + 1); |
| gc.drawLine(area.x + area.width - 1, area.y + 1, area.x |
| + area.width - 1, area.y + area.height - 1); |
| gc.drawLine(area.x + 1, area.y + 1, area.x + 1, area.y |
| + area.height - 1); |
| } |
| }); |
| |
| // Create the ToolBarManager and add all the actions to it |
| ToolBarManager tbMgr = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL); |
| List actions = getOutlineActions(); |
| for (int i = 0; i < actions.size(); i++) { |
| tbMgr.add(new ToolbarDropdownContributionItem(((IAction) actions |
| .get(i)))); |
| } |
| tbMgr.createControl(composite); |
| tbMgr.getControl().setFont(composite.getFont()); |
| |
| // By default, the ToolBarManager does not set text on ToolItems. Since, |
| // we want to display the text, we will have to do it manually. |
| ToolItem[] items = tbMgr.getControl().getItems(); |
| for (int i = 0; i < items.length; i++) { |
| ToolItem item = items[i]; |
| item.setText(((IAction) actions.get(i)).getText()); |
| } |
| |
| return tbMgr.getControl(); |
| } |
| |
| /** |
| * Creates the TreeViewer that is the outline of the model. |
| * |
| * @param composite |
| * The Composite to which the ToolBar is to be added |
| * @return The newly created TreeViewer |
| */ |
| protected TreeViewer createOutlineTreeViewer(Composite composite) { |
| Tree treeForViewer = new Tree(composite, SWT.BORDER); |
| treeForViewer.setFont(composite.getFont()); |
| GridData data = new GridData(GridData.FILL_VERTICAL |
| | GridData.HORIZONTAL_ALIGN_FILL); |
| data.widthHint = 185; |
| // Make the tree this tall even when there is nothing in it. This will |
| // keep the |
| // dialog from shrinking to an unusually small size. |
| data.heightHint = 200; |
| treeForViewer.setLayoutData(data); |
| TreeViewer viewer = new TreeViewer(treeForViewer) { |
| protected void preservingSelection(Runnable updateCode) { |
| if ((getTree().getStyle() & SWT.SINGLE) != 0) |
| updateCode.run(); |
| else |
| super.preservingSelection(updateCode); |
| } |
| }; |
| viewer.setContentProvider(new PaletteTreeProvider(viewer)); |
| treeViewerLabelProvider = new PaletteLabelProvider(viewer); |
| viewer.setLabelProvider(treeViewerLabelProvider); |
| viewer.setInput(getPaletteRoot()); |
| viewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| handleOutlineSelectionChanged(); |
| } |
| }); |
| |
| return viewer; |
| } |
| |
| /** |
| * Creates the part of the dialog where the properties of the element |
| * selected in the outline will be displayed. |
| * |
| * <p> |
| * The properties panel contains the following: |
| * <UL> |
| * <LI>Title ({@link #createPropertiesPanelTitle(Composite)})</LI> |
| * </UL> |
| * The rest of the panel is constructed in this method. |
| * </p> |
| * |
| * @param container |
| * The Composite to which this part is to be added |
| * @return The properties panel |
| */ |
| protected Control createPropertiesPanel(Composite container) { |
| Composite composite = new Composite(container, SWT.NONE); |
| composite.setFont(container.getFont()); |
| GridLayout layout = new GridLayout(1, false); |
| layout.horizontalSpacing = 0; |
| layout.marginWidth = 0; |
| layout.marginHeight = 0; |
| layout.verticalSpacing = 0; |
| composite.setLayout(layout); |
| |
| titleSwitcher = createPropertiesPanelTitle(composite); |
| |
| propertiesPanelContainer = new PageBook(composite, SWT.NONE); |
| propertiesPanelContainer.setFont(composite.getFont()); |
| GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL |
| | GridData.FILL_VERTICAL); |
| data.horizontalSpan = 2; |
| propertiesPanelContainer.setLayoutData(data); |
| propertiesPanelContainer.addListener(SWT.Resize, new Listener() { |
| public void handleEvent(Event event) { |
| if (activePage != null) { |
| propertiesPanelContainer.layout(); |
| } |
| } |
| }); |
| |
| return composite; |
| } |
| |
| /** |
| * Creates the title for the properties panel. It is a PageBook that can |
| * switch between showing the regular title (the selected entry's label and |
| * icon) and an error message if an error has occured. |
| * |
| * @param parent |
| * The parent composite |
| * @return The newly created PageBook title |
| */ |
| protected PageBook createPropertiesPanelTitle(Composite parent) { |
| GridLayout layout; |
| PageBook book = new PageBook(parent, SWT.NONE); |
| book.setFont(parent.getFont()); |
| book.setLayoutData(new GridData(GridData.FILL_HORIZONTAL |
| | GridData.VERTICAL_ALIGN_FILL)); |
| |
| titlePage = new Composite(book, SWT.NONE); |
| titlePage.setFont(book.getFont()); |
| layout = new GridLayout(2, false); |
| layout.horizontalSpacing = 0; |
| layout.marginWidth = 0; |
| layout.marginHeight = 0; |
| layout.verticalSpacing = 0; |
| titlePage.setLayout(layout); |
| title = createSectionTitle(titlePage, |
| PaletteMessages.NO_SELECTION_TITLE); |
| |
| errorPage = new Composite(book, SWT.NONE); |
| errorPage.setFont(book.getFont()); |
| layout = new GridLayout(1, false); |
| layout.horizontalSpacing = 0; |
| layout.marginWidth = 0; |
| layout.marginHeight = 0; |
| layout.verticalSpacing = 0; |
| errorPage.setLayout(layout); |
| Composite intermediary = new Composite(errorPage, SWT.NONE) { |
| public Point computeSize(int wHint, int hHint, boolean changed) { |
| Rectangle bounds = title.getBounds(); |
| return new Point(bounds.width, bounds.height); |
| } |
| }; |
| intermediary.setLayoutData(new GridData(GridData.FILL_HORIZONTAL |
| | GridData.VERTICAL_ALIGN_FILL)); |
| StackLayout stackLayout = new StackLayout(); |
| intermediary.setLayout(stackLayout); |
| errorTitle = new MultiLineLabel(intermediary); |
| stackLayout.topControl = errorTitle; |
| errorTitle.setImage(JFaceResources.getImage(DLG_IMG_MESSAGE_ERROR)); |
| errorTitle.setFont(errorPage.getFont()); |
| Label separator = new Label(errorPage, SWT.SEPARATOR | SWT.HORIZONTAL); |
| separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| |
| book.showPage(titlePage); |
| return book; |
| } |
| |
| /** |
| * A convenient method to create CLabel titles (like the ones used in the |
| * Preferences dialog in the Eclipse workbench) throughout the dialog. |
| * |
| * @param composite |
| * The composite in which the title is to be created (it must |
| * have a GridLayout with two columns). |
| * @param text |
| * The title to be displayed |
| * @return The newly created CLabel for convenience |
| */ |
| protected CLabel createSectionTitle(Composite composite, String text) { |
| CLabel cTitle = new CLabel(composite, SWT.LEFT); |
| Color background = JFaceColors.getBannerBackground(composite |
| .getDisplay()); |
| Color foreground = JFaceColors.getBannerForeground(composite |
| .getDisplay()); |
| JFaceColors.setColors(cTitle, foreground, background); |
| cTitle.setFont(JFaceResources.getBannerFont()); |
| cTitle.setText(text); |
| cTitle.setLayoutData(new GridData(GridData.FILL_HORIZONTAL |
| | GridData.VERTICAL_ALIGN_FILL)); |
| |
| if (titleImage == null) { |
| titleImage = new Image(composite.getDisplay(), ImageDescriptor |
| .createFromFile(Internal.class, |
| "icons/customizer_dialog_title.gif").getImageData()); //$NON-NLS-1$ |
| composite.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| titleImage.dispose(); |
| titleImage = null; |
| } |
| }); |
| } |
| |
| Label imageLabel = new Label(composite, SWT.LEFT); |
| imageLabel.setBackground(background); |
| imageLabel.setImage(titleImage); |
| imageLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL |
| | GridData.VERTICAL_ALIGN_FILL)); |
| |
| Label separator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); |
| GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); |
| data.horizontalSpan = 2; |
| separator.setLayoutData(data); |
| |
| return cTitle; |
| } |
| |
| /** |
| * Returns the Button with the given id; or <code>null</code> if none was |
| * found. |
| * |
| * @see org.eclipse.jface.dialogs.Dialog#getButton(int) |
| */ |
| protected Button getButton(int id) { |
| Button button = null; |
| Widget widget = getWidget(id); |
| if (widget instanceof Button) { |
| button = (Button) widget; |
| } |
| |
| return button; |
| } |
| |
| /** |
| * @return The customizer that is responsible for handling the various tasks |
| * and updating the model. |
| */ |
| protected PaletteCustomizer getCustomizer() { |
| return customizer; |
| } |
| |
| /** |
| * Returns the <code>EntryPage</code> for the given |
| * <code>PaletteEntry</code> . The <code>EntryPage</code> is retrieved from |
| * the customizer. If the given entry is <code>null</code>, |
| * <code>null</code> will be returned. If the customizer returns |
| * <code>null</code> for the valid entry, a default page will be created and |
| * returned. |
| * |
| * @param entry |
| * The PaletteEntry whose properties need to be displayed |
| * @return The EntryPage with the properties of the given PaletteEntry |
| */ |
| protected EntryPage getEntryPage(PaletteEntry entry) { |
| if (entry == null) { |
| return null; |
| } |
| |
| if (entriesToPages.containsKey(entry)) { |
| return (EntryPage) entriesToPages.get(entry); |
| } |
| |
| EntryPage page = getCustomizer().getPropertiesPage(entry); |
| if (page == null) { |
| page = new DefaultEntryPage(); |
| } |
| page.createControl(propertiesPanelContainer, entry); |
| page.setPageContainer(this); |
| entriesToPages.put(entry, page); |
| |
| return page; |
| } |
| |
| /** |
| * Provides access to the actions that are used to manipulate the model. The |
| * actions will be created, if they haven't been yet. |
| * |
| * @return the list of <code>PaletteCustomizationAction</code>s |
| * @see #createOutlineActions() |
| */ |
| protected final List getOutlineActions() { |
| if (actions == null) { |
| actions = createOutlineActions(); |
| } |
| return actions; |
| } |
| |
| /** |
| * Provides sub-classes with access to the PaletteRoot |
| * |
| * @return the palette root |
| */ |
| protected PaletteRoot getPaletteRoot() { |
| return root; |
| } |
| |
| /** |
| * @return The PaletteEntry that is currently selected in the Outline Tree; |
| * <code>null</code> if none is selected |
| */ |
| protected PaletteEntry getSelectedPaletteEntry() { |
| TreeItem item = getSelectedTreeItem(); |
| if (item != null) { |
| return (PaletteEntry) item.getData(); |
| } |
| return null; |
| |
| } |
| |
| /** |
| * @return The TreeItem that is currently selected in the Outline Tree; |
| * <code>null</code> if none is selected |
| */ |
| protected TreeItem getSelectedTreeItem() { |
| TreeItem[] items = tree.getSelection(); |
| if (items.length > 0) { |
| return items[0]; |
| } |
| return null; |
| } |
| |
| /** |
| * The <code>Widget</code>s that were created with a unique ID and added to |
| * this class' internal map can be retrieved through this method. |
| * |
| * @param id |
| * The unique ID of the Widget that you wish to retrieve |
| * @return The Widget, if one with the given id exists; <code>null</code> |
| * otherwise |
| */ |
| protected Widget getWidget(int id) { |
| Widget widget = (Widget) widgets.get(new Integer(id)); |
| if (widget == null) { |
| widget = super.getButton(id); |
| } |
| |
| return widget; |
| } |
| |
| /** |
| * This method is invoked when the Apply button is pressed |
| * <p> |
| * IMPORTANT: It is recommended that you not override this method. Closing |
| * the dialog with the 'X' at the top right of the window, or by hitting |
| * 'Esc' or any other way, corresponds to a "Cancel." That will, however, |
| * not result in this method being invoked. To handle such cases, saving or |
| * rejecting the changes is handled in {@link #close()}. Override |
| * {@link #save()} and {@link #revertToSaved()} to add to what needs to be |
| * done when saving or cancelling. |
| * </p> |
| */ |
| protected final void handleApplyPressed() { |
| save(); |
| } |
| |
| /** |
| * This method is called when the "Delete" action is run (either through the |
| * context menu or the toolbar). It deletes the selected palette entry. |
| */ |
| protected void handleDelete() { |
| getCustomizer().performDelete(getSelectedPaletteEntry()); |
| handleOutlineSelectionChanged(); |
| } |
| |
| /** |
| * This method is called when the "Move Down" action is run (either through |
| * the context menu or the toolbar). It moves the selected palette entry |
| * down. |
| */ |
| protected void handleMoveDown() { |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| getCustomizer().performMoveDown(entry); |
| treeviewer.setSelection(new StructuredSelection(entry), true); |
| updateActions(); |
| } |
| |
| /** |
| * This method is called when the "Move Up" action is run (either through |
| * the context menu or the toolbar). It moves the selected entry up. |
| */ |
| protected void handleMoveUp() { |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| getCustomizer().performMoveUp(entry); |
| treeviewer.setSelection(new StructuredSelection(entry), true); |
| updateActions(); |
| } |
| |
| /** |
| * This is the method that is called everytime the selection in the outline |
| * (treeviewer) changes. |
| */ |
| protected void handleOutlineSelectionChanged() { |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| |
| if (activeEntry == entry) { |
| return; |
| } |
| |
| if (errorMessage != null) { |
| MessageDialog dialog = new MessageDialog(getShell(), |
| PaletteMessages.ERROR, |
| null, |
| PaletteMessages.ABORT_PAGE_FLIPPING_MESSAGE |
| + "\n" + errorMessage, //$NON-NLS-1$ |
| MessageDialog.ERROR, |
| new String[] { IDialogConstants.get().OK_LABEL }, 0); |
| dialog.open(); |
| treeviewer.addPostSelectionChangedListener(pageFlippingPreventer); |
| } else { |
| setActiveEntry(entry); |
| } |
| updateActions(); |
| } |
| |
| /** |
| * This method is invoked when the changes made since the last save need to |
| * be cancelled. |
| */ |
| protected void revertToSaved() { |
| getCustomizer().revertToSaved(); |
| } |
| |
| /** |
| * This method is invoked when the changes made since the last save need to |
| * be saved. |
| */ |
| protected void save() { |
| if (activePage != null) { |
| activePage.apply(); |
| } |
| getCustomizer().save(); |
| } |
| |
| /** |
| * This methods sets the active entry. Based on the selection, this method |
| * will appropriately enable or disable the ToolBar items, will change the |
| * CLabel heading of the propreties panel, and will show the properties of |
| * the selected item in the properties panel. |
| * |
| * @param entry |
| * The new active entry, i.e., the new selected entry (it can be |
| * <code>null</code>) |
| */ |
| protected void setActiveEntry(PaletteEntry entry) { |
| if (activeEntry != null) { |
| activeEntry.removePropertyChangeListener(titleUpdater); |
| } |
| |
| activeEntry = entry; |
| |
| if (entry != null) { |
| title.setText(entry.getLabel()); |
| Image img = treeViewerLabelProvider.getImage(entry); |
| if (img == null) { |
| img = getSelectedTreeItem().getImage(); |
| } |
| title.setImage(img); |
| entry.addPropertyChangeListener(titleUpdater); |
| EntryPage panel = getEntryPage(entry); |
| setActiveEntryPage(panel); |
| } else { |
| title.setImage(null); |
| title.setText(PaletteMessages.NO_SELECTION_TITLE); |
| // Lazy creation |
| if (noSelectionPage == null) { |
| noSelectionPage = new EntryPage() { |
| private Text text; |
| |
| public void apply() { |
| } |
| |
| public void createControl(Composite parent, |
| PaletteEntry entry) { |
| text = new Text(parent, SWT.READ_ONLY); |
| text.setFont(parent.getFont()); |
| text.setText(PaletteMessages.NO_SELECTION_MADE); |
| } |
| |
| public Control getControl() { |
| return text; |
| } |
| |
| public void setPageContainer( |
| EntryPageContainer pageContainer) { |
| } |
| }; |
| noSelectionPage.createControl(propertiesPanelContainer, null); |
| } |
| setActiveEntryPage(noSelectionPage); |
| } |
| } |
| |
| /** |
| * Sets the given EntryPage as the top page in the PageBook that shows the |
| * properties of the item selected in the Outline. If the given EntryPage is |
| * null, nothing will be shown. |
| * |
| * @param page |
| * The EntryPage to be shown |
| */ |
| protected void setActiveEntryPage(EntryPage page) { |
| // Have the currently displayed page save its changes |
| if (activePage != null) { |
| activePage.apply(); |
| } |
| |
| if (page == null) { |
| // No page available to display, so hide the panel container |
| propertiesPanelContainer.setVisible(false); |
| } else { |
| // Show the page and grow the shell, if necessary, so that the page |
| // is |
| // completely visible |
| Point oldSize = getShell().getSize(); |
| propertiesPanelContainer.showPage(page.getControl()); |
| |
| /* |
| * Fix for bug #34748 There's no need to resize the Shell if |
| * initializeBounds() hasn't been called yet. It will automatically |
| * resize the Shell so that everything fits in the Dialog. After |
| * that, we can resize the Shell whenever there's an entry page that |
| * cannot fit in the dialog. |
| */ |
| if (!isSetup) { |
| Point newSize = getShell().computeSize(SWT.DEFAULT, |
| SWT.DEFAULT, true); |
| int x = newSize.x - oldSize.x; |
| x = (x < 0) ? 0 : x; |
| int y = newSize.y - oldSize.y; |
| y = (y < 0) ? 0 : y; |
| if (x > 0 || y > 0) { |
| getShell().setSize(oldSize.x + x, oldSize.y + y); |
| } |
| } |
| |
| // Show the property panel container if it was hidden |
| if (!propertiesPanelContainer.isVisible()) { |
| propertiesPanelContainer.setVisible(true); |
| } |
| } |
| |
| activePage = page; |
| } |
| |
| /** |
| * Sets the given PaletteEntry as the one to be selected when the dialog |
| * opens. It is discarded when the dialog is closed. |
| * |
| * @param entry |
| * The PaletteEntry that should be selected when the dialog is |
| * opened |
| */ |
| public void setDefaultSelection(PaletteEntry entry) { |
| initialSelection = entry; |
| } |
| |
| /** |
| * This method should be invoked by EntryPages when there is an error. It |
| * will show the given error in the title of the properties panel. OK and |
| * Apply buttons will be disabled. Selecting some other entry in the outline |
| * tree will not be allowed until the error is fixed. |
| * |
| * @see org.eclipse.gef.ui.palette.customize.EntryPageContainer#showProblem(String) |
| */ |
| public void showProblem(String error) { |
| Assert.isNotNull(error); |
| errorTitle.setText(error); |
| titleSwitcher.showPage(errorPage); |
| getButton(IDialogConstants.OK_ID).setEnabled(false); |
| getButton(APPLY_ID).setEnabled(false); |
| errorMessage = error; |
| } |
| |
| /** |
| * Updates the actions created in {@link #createOutlineActions()}, enabling |
| * or disabling them as necessary. |
| */ |
| protected void updateActions() { |
| List actions = getOutlineActions(); |
| for (Iterator iter = actions.iterator(); iter.hasNext();) { |
| PaletteCustomizationAction action = (PaletteCustomizationAction) iter |
| .next(); |
| action.update(); |
| } |
| } |
| |
| /* |
| * Delete Action |
| */ |
| private class DeleteAction extends PaletteCustomizationAction { |
| public DeleteAction() { |
| setEnabled(false); |
| setText(PaletteMessages.DELETE_LABEL); |
| ISharedImages sharedImages = PlatformUI.getWorkbench() |
| .getSharedImages(); |
| setImageDescriptor(sharedImages |
| .getImageDescriptor(ISharedImages.IMG_TOOL_DELETE)); |
| setDisabledImageDescriptor(sharedImages |
| .getImageDescriptor(ISharedImages.IMG_TOOL_DELETE_DISABLED)); |
| } |
| |
| public void run() { |
| handleDelete(); |
| } |
| |
| public void update() { |
| boolean enabled = false; |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| if (entry != null) { |
| enabled = getCustomizer().canDelete(entry); |
| } |
| setEnabled(enabled); |
| } |
| } |
| |
| /* |
| * Move Down Action |
| */ |
| private class MoveDownAction extends PaletteCustomizationAction { |
| public MoveDownAction() { |
| setEnabled(false); |
| setText(PaletteMessages.MOVE_DOWN_LABEL); |
| setImageDescriptor(ImageDescriptor.createFromFile(Internal.class, |
| "icons/next_nav.gif"));//$NON-NLS-1$ |
| setDisabledImageDescriptor(ImageDescriptor.createFromFile( |
| Internal.class, "icons/move_down_disabled.gif"));//$NON-NLS-1$ |
| } |
| |
| public void run() { |
| handleMoveDown(); |
| } |
| |
| public void update() { |
| boolean enabled = false; |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| if (entry != null) { |
| enabled = getCustomizer().canMoveDown(entry); |
| } |
| setEnabled(enabled); |
| } |
| } |
| |
| /* |
| * Move Up Action |
| */ |
| private class MoveUpAction extends PaletteCustomizationAction { |
| public MoveUpAction() { |
| setEnabled(false); |
| setText(PaletteMessages.MOVE_UP_LABEL); |
| setImageDescriptor(ImageDescriptor.createFromFile(Internal.class, |
| "icons/prev_nav.gif"));//$NON-NLS-1$ |
| setDisabledImageDescriptor(ImageDescriptor.createFromFile( |
| Internal.class, "icons/move_up_disabled.gif")); //$NON-NLS-1$ |
| } |
| |
| public void run() { |
| handleMoveUp(); |
| } |
| |
| public void update() { |
| boolean enabled = false; |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| if (entry != null) { |
| enabled = getCustomizer().canMoveUp(entry); |
| } |
| setEnabled(enabled); |
| } |
| } |
| |
| /* |
| * New Action |
| */ |
| private class NewAction extends PaletteCustomizationAction implements |
| IMenuCreator { |
| private List factories; |
| private MenuManager menuMgr; |
| |
| public NewAction() { |
| factories = wrap(getCustomizer().getNewEntryFactories()); |
| if (factories.isEmpty()) { |
| setEnabled(false); |
| } else { |
| setMenuCreator(this); |
| } |
| |
| setText(PaletteMessages.NEW_LABEL); |
| setImageDescriptor(ImageDescriptor.createFromFile(Internal.class, |
| "icons/add.gif")); //$NON-NLS-1$ |
| setDisabledImageDescriptor(ImageDescriptor.createFromFile( |
| Internal.class, "icons/add-disabled.gif")); //$NON-NLS-1$ |
| } |
| |
| private void addActionToMenu(Menu parent, IAction action) { |
| ActionContributionItem item = new ActionContributionItem(action); |
| item.fill(parent, -1); |
| } |
| |
| public void dispose() { |
| if (menuMgr != null) { |
| menuMgr.dispose(); |
| menuMgr = null; |
| } |
| } |
| |
| public Menu getMenu(Control parent) { |
| // Create the menu manager and add all the NewActions to it |
| if (menuMgr == null) { |
| // Lazily create the manager |
| menuMgr = new MenuManager(); |
| menuMgr.createContextMenu(parent); |
| } |
| |
| updateMenuManager(menuMgr); |
| return menuMgr.getMenu(); |
| } |
| |
| public Menu getMenu(Menu parent) { |
| Menu menu = new Menu(parent); |
| for (Iterator iter = factories.iterator(); iter.hasNext();) { |
| FactoryWrapperAction action = (FactoryWrapperAction) iter |
| .next(); |
| if (action.isEnabled()) { |
| addActionToMenu(menu, action); |
| } |
| } |
| |
| return menu; |
| } |
| |
| public void run() { |
| } |
| |
| public void update() { |
| boolean enabled = false; |
| PaletteEntry entry = getSelectedPaletteEntry(); |
| if (entry == null) { |
| entry = getPaletteRoot(); |
| } |
| // Enable or disable the FactoryWrapperActions |
| for (Iterator iter = factories.iterator(); iter.hasNext();) { |
| FactoryWrapperAction action = (FactoryWrapperAction) iter |
| .next(); |
| action.setEnabled(action.canCreate(entry)); |
| enabled = enabled || action.isEnabled(); |
| } |
| |
| // Enable this action IFF at least one of the new actions is enabled |
| setEnabled(enabled); |
| } |
| |
| protected void updateMenuManager(MenuManager manager) { |
| manager.removeAll(); |
| for (Iterator iter = factories.iterator(); iter.hasNext();) { |
| FactoryWrapperAction action = (FactoryWrapperAction) iter |
| .next(); |
| if (action.isEnabled()) { |
| manager.add(action); |
| } |
| } |
| } |
| |
| private List wrap(List list) { |
| List newList = new ArrayList(); |
| if (list != null) { |
| for (Iterator iter = list.iterator(); iter.hasNext();) { |
| PaletteEntryFactory element = (PaletteEntryFactory) iter |
| .next(); |
| newList.add(new FactoryWrapperAction(element)); |
| } |
| } |
| |
| return newList; |
| } |
| } |
| |
| /* |
| * FactoryWrapperAction class |
| */ |
| private class FactoryWrapperAction extends Action { |
| private PaletteEntryFactory factory; |
| |
| public FactoryWrapperAction(PaletteEntryFactory factory) { |
| this.factory = factory; |
| setText(factory.getLabel()); |
| setImageDescriptor(factory.getImageDescriptor()); |
| setHoverImageDescriptor(factory.getImageDescriptor()); |
| } |
| |
| public boolean canCreate(PaletteEntry entry) { |
| return factory.canCreate(entry); |
| } |
| |
| public void run() { |
| PaletteEntry selected = getSelectedPaletteEntry(); |
| if (selected == null) |
| selected = getPaletteRoot(); |
| PaletteEntry newEntry = factory |
| .createNewEntry(getShell(), selected); |
| treeviewer.setSelection(new StructuredSelection(newEntry), true); |
| updateActions(); |
| } |
| } |
| |
| } |