/******************************************************************************* * Copyright (c) 2000, 2007 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.SWT; 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.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.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.core.runtime.Assert; 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.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 composite.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { Rectangle area = composite.getBounds(); GC gc = e.gc; 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.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(); } } }