/*******************************************************************************
* Copyright (c) 2000, 2009 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;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IMemento;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.IFigure;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.editparts.SimpleRootEditPart;
import org.eclipse.gef.internal.ui.palette.PaletteSelectionTool;
import org.eclipse.gef.internal.ui.palette.editparts.DrawerEditPart;
import org.eclipse.gef.internal.ui.palette.editparts.PaletteStackEditPart;
import org.eclipse.gef.internal.ui.palette.editparts.ToolEntryEditPart;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.gef.palette.PaletteListener;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PaletteStack;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.ui.palette.customize.PaletteCustomizerDialog;
import org.eclipse.gef.ui.palette.editparts.PaletteEditPart;
import org.eclipse.gef.ui.parts.PaletteViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
/**
* Graphical viewer for the GEF palette.
*
* @author Randy Hudson
* @author Pratik Shah
*/
public class PaletteViewer
extends ScrollingGraphicalViewer
{
private class PreferenceListener
implements PropertyChangeListener
{
/**
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt) {
String property = evt.getPropertyName();
EditPart root = getRootEditPart().getContents();
if (property.equals(PaletteViewerPreferences.PREFERENCE_FONT)) {
updateFont();
refreshAllEditParts(root);
} else if (property.equals(PaletteViewerPreferences.PREFERENCE_LAYOUT)
|| property.equals(PaletteViewerPreferences.PREFERENCE_AUTO_COLLAPSE)
|| property.equals(DefaultPaletteViewerPreferences
.convertLayoutToPreferenceName(getPaletteViewerPreferences()
.getLayoutSetting()))) {
refreshAllEditParts(root);
}
}
private void refreshAllEditParts(EditPart part) {
part.refresh();
List children = part.getChildren();
for (Iterator iter = children.iterator(); iter.hasNext();) {
EditPart child = (EditPart) iter.next();
refreshAllEditParts(child);
}
}
}
private static final PaletteViewerPreferences PREFERENCE_STORE =
new DefaultPaletteViewerPreferences();
private ToolEntry activeEntry = null;
private PaletteCustomizer customizer = null;
private PaletteCustomizerDialog customizerDialog = null;
private boolean globalScrollbar = false;
private List paletteListeners = new ArrayList();
private PaletteRoot paletteRoot = null;
private PreferenceListener prefListener = new PreferenceListener();
private PaletteViewerPreferences prefs = PREFERENCE_STORE;
private Font font = null;
/**
* Constructor
*/
public PaletteViewer() {
EditDomain domain = new EditDomain();
domain.setDefaultTool(new PaletteSelectionTool());
domain.loadDefaultTool();
setEditDomain(domain);
setKeyHandler(new PaletteViewerKeyHandler(this));
setEditPartFactory(new PaletteEditPartFactory());
}
/**
* Adds the given PaletteListener as the one to be notified when the active tool on the
* palette changes.
*
* @param paletteListener The listener that needs to be notified of active tool
* changes on the palette
*/
public void addPaletteListener(PaletteListener paletteListener) {
if (paletteListeners != null)
paletteListeners.add(paletteListener);
}
/**
* @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#createDefaultRoot()
*/
protected void createDefaultRoot() {
setRootEditPart(new SimpleRootEditPart());
}
private void disposeFont() {
if (font != null) {
font.dispose();
font = null;
}
}
/**
* Indicates that the palette should scroll using a native vertical scrollbar as opposed
* to individual lightweight buttons that appear dynamically on each drawer. The default
* settings is <code>false</code>. Enabling this setting requires additional horizontal
* screen space for the scrollbar. Therefore, its use is discouraged.
* <p>
* This setting must be changed prior to calling
* {@link ScrollingGraphicalViewer#createControl(org.eclipse.swt.widgets.Composite)}.
* After the control is created, changing this setting will have no effect.
* </p>
*
* @param value <code>true</code> if a vertical scrollbar should be displayed
*/
public void enableVerticalScrollbar(boolean value) {
this.globalScrollbar = value;
}
/**
* @return the DrawerEditPart that has the given part; part, if part is a drawer; null, if
* part is not in a drawer
*/
private DrawerEditPart findContainingDrawer(EditPart part) {
if (part == null)
return null;
if (part instanceof DrawerEditPart)
return (DrawerEditPart)part;
return findContainingDrawer(part.getParent());
}
/**
* Notifies registered listeners of change in the active tool on the palette
*/
protected void fireModeChanged() {
if (paletteListeners == null)
return;
for (int listener = 0; listener < paletteListeners.size(); listener++)
((PaletteListener) paletteListeners.get(listener))
.activeToolChanged(this, activeEntry);
}
/**
* @return the customizer
*/
public PaletteCustomizer getCustomizer() {
return customizer;
}
/**
* NOTE: A PaletteCustomizer must be set for this viewer using the
* {@link #setCustomizer(PaletteCustomizer)} method before this method is invoked.
*
* @return The dialog that can be used to customize entries on the palette
*/
public PaletteCustomizerDialog getCustomizerDialog() {
if (customizerDialog == null) {
customizerDialog = new PaletteCustomizerDialog(
getControl().getShell(), getCustomizer(), getPaletteRoot());
}
return customizerDialog;
}
/**
* @return the entry for the currently active tool
*/
public ToolEntry getActiveTool() {
return activeEntry;
}
/**
* Returns the palette's root model.
* @return the palette root
*/
public PaletteRoot getPaletteRoot() {
return paletteRoot;
}
/**
* @return The PaletteViewerPreferences that this palette is using to store its
* preferences (if none has been set, it returns the default one, which uses
* the GEF preference store)
*/
public PaletteViewerPreferences getPaletteViewerPreferences() {
return prefs;
}
private ToolEntryEditPart getToolEntryEditPart(ToolEntry entry) {
return (ToolEntryEditPart)getEditPartRegistry().get(entry);
}
/**
* @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#handleDispose(
* org.eclipse.swt.events.DisposeEvent)
*/
protected void handleDispose(DisposeEvent e) {
super.handleDispose(e);
disposeFont();
}
/**
* @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#handleFocusGained(FocusEvent)
*/
protected void handleFocusGained(FocusEvent fe) {
super.handleFocusGained(fe);
/*
* Fix for Bug# 63297
* When the palette gets focus, the LWS will take care of setting focus on the
* correct figure. However, when the user clicks on an entry, the focus is "thrown
* away." In that case, the LWS would set focus on the first focusable figure. We
* override that here and set focus on the correct/selected figure. Invoking
* getFocusEditPart().setFocus(true) does not work because that editpart thinks it
* already has focus (it's not aware of focus being thrown away) and does nothing.
*/
if (focusPart == null) {
IFigure fig = ((GraphicalEditPart)getFocusEditPart()).getFigure();
fig.internalGetEventDispatcher().requestFocus(fig);
}
}
/**
* @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#hookControl()
*/
protected void hookControl() {
super.hookControl();
FigureCanvas canvas = getFigureCanvas();
canvas.getViewport().setContentsTracksWidth(true);
canvas.getViewport().setContentsTracksHeight(!globalScrollbar);
canvas.setHorizontalScrollBarVisibility(FigureCanvas.NEVER);
canvas.setVerticalScrollBarVisibility(
globalScrollbar ? FigureCanvas.ALWAYS : FigureCanvas.AUTOMATIC);
if (prefs != null)
prefs.addPropertyChangeListener(prefListener);
updateFont();
}
/**
* Returns true if the given PaletteDrawer is expanded
* @param drawer the PaletteDrawer
* @return true if expanded
*/
public boolean isExpanded(PaletteDrawer drawer) {
EditPart ep = (EditPart)getEditPartRegistry().get(drawer);
if (ep instanceof DrawerEditPart)
return ((DrawerEditPart)ep).isExpanded();
return false;
}
/**
* Returns true if the given PaletteDrawer is pinned
* @param drawer the PaletteDrawer
* @return true if pinned
*/
public boolean isPinned(PaletteDrawer drawer) {
EditPart ep = (EditPart)getEditPartRegistry().get(drawer);
if (ep instanceof DrawerEditPart)
return ((DrawerEditPart)ep).isPinnedOpen();
return false;
}
/**
* The given PaletteListener will not be notified of active tool changes in the palette.
*
* @param paletteListener the PaletteListener which doesn't want to be notified of
* active tool changes in the palette anymore
*/
public void removePaletteListener(PaletteListener paletteListener) {
paletteListeners.remove(paletteListener);
}
/**
* Tries to apply the state of the given IMemento to the contents of this viewer. It
* fails silently, i.e. no exceptions are thrown if the given state could not be applied.
*
* @param memento The memento that has the state to be applied to the contents of this
* viewer
* @return a boolean indicating whether or not the given memento was successfully applied
* @since 3.0
*/
public boolean restoreState(IMemento memento) {
try {
PaletteEditPart part = (PaletteEditPart)getEditPartRegistry()
.get(getPaletteRoot());
if (part != null)
part.restoreState(memento);
} catch (RuntimeException re) {
/*
* @TODO:Pratik Perhaps you should log this exception. Or not catch it at all.
*/
return false;
}
return true;
}
/**
* @see ScrollingGraphicalViewer#reveal(EditPart)
*/
public void reveal(EditPart part) {
// If the given part is a drawer, we don't need to expand it. Hence, when invoking
// findContainingDrawer(), we use part.getParent()
DrawerEditPart drawer = findContainingDrawer(part.getParent());
if (drawer != null && !drawer.isExpanded())
drawer.setExpanded(true);
// if the part is inside a stack, set it to be the top level item of the stack.
if (part.getParent() != null && part.getParent() instanceof PaletteStackEditPart)
((PaletteStack)part.getParent().getModel()).setActiveEntry((PaletteEntry)part.getModel());
super.reveal(part);
}
/**
* Captures the state of the contents of this viewer in the given memento
* @param memento the IMemento in which the state is to be saved
* @since 3.0
*/
public void saveState(IMemento memento) {
// Bug# 69026 - The PaletteRoot can be null initially for VEP
PaletteEditPart base = (PaletteEditPart)getEditPartRegistry().get(getPaletteRoot());
if (base != null)
base.saveState(memento);
}
/**
* Sets the customizer.
*
* @param customizer the customizer to be set
*/
public void setCustomizer(PaletteCustomizer customizer) {
this.customizer = customizer;
}
/**
* Sets the active entry for this palette. The Editpart for the given entry will be
* activated (selected).
*
* @param newMode the ToolEntry whose EditPart has to be set as the active tool
* in this palette
*/
public void setActiveTool(ToolEntry newMode) {
if (newMode == null)
newMode = getPaletteRoot().getDefaultEntry();
if (activeEntry != null)
getToolEntryEditPart(activeEntry).setToolSelected(false);
activeEntry = newMode;
if (activeEntry != null) {
ToolEntryEditPart editpart = getToolEntryEditPart(activeEntry);
if (editpart != null) {
editpart.setToolSelected(true);
}
}
fireModeChanged();
}
/**
* Sets the root for this palette.
* @param root the PaletteRoot for this palette
*/
public void setPaletteRoot(PaletteRoot root) {
if (root == paletteRoot)
return;
paletteRoot = root;
if (paletteRoot != null) {
EditPart palette =
getEditPartFactory().createEditPart(getRootEditPart(), root);
getRootEditPart().setContents(palette);
}
}
/**
* This palette will use the given PaletteViewerPreferences to store all its preferences.
* <p>
* NOTE: This method should be invoked by a client only once (before the first time
* {@link #getPaletteViewerPreferences()} is invoked). Trying to invoke this method
* after that could lead to problems where some preferences would still be stored in the
* old preference store.
* </p>
*
* @param prefs the PaletteViewerPreferences that is to be used to store all the
* preferences for this palette
*/
public void setPaletteViewerPreferences(PaletteViewerPreferences prefs) {
if (this.prefs != null)
this.prefs.removePropertyChangeListener(prefListener);
this.prefs = prefs;
if (getControl() != null && !getControl().isDisposed())
this.prefs.addPropertyChangeListener(prefListener);
}
/**
* @see org.eclipse.gef.ui.parts.AbstractEditPartViewer#unhookControl()
*/
protected void unhookControl() {
super.unhookControl();
disposeFont();
if (prefs != null)
prefs.removePropertyChangeListener(prefListener);
}
private void updateFont() {
disposeFont();
if (getControl() == null || getControl().isDisposed())
return;
font = new Font(Display.getCurrent(), getPaletteViewerPreferences().getFontData());
getControl().setFont(font);
getFigureCanvas().getViewport().invalidateTree();
getFigureCanvas().getViewport().revalidate();
getFigureCanvas().redraw();
}
}