/******************************************************************************* * 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.internal.ui.palette.editparts; import java.beans.PropertyChangeEvent; import java.util.Iterator; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.jface.action.MenuManager; import org.eclipse.draw2d.ActionEvent; import org.eclipse.draw2d.ActionListener; import org.eclipse.draw2d.Border; import org.eclipse.draw2d.BorderLayout; import org.eclipse.draw2d.ButtonBorder; import org.eclipse.draw2d.ButtonModel; import org.eclipse.draw2d.ChangeEvent; import org.eclipse.draw2d.ChangeListener; import org.eclipse.draw2d.Clickable; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Figure; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.StackLayout; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.palette.PaletteEntry; import org.eclipse.gef.palette.PaletteListener; import org.eclipse.gef.palette.PaletteStack; import org.eclipse.gef.palette.ToolEntry; import org.eclipse.gef.ui.actions.SetActivePaletteToolAction; import org.eclipse.gef.ui.palette.PaletteViewer; import org.eclipse.gef.ui.palette.editparts.PaletteEditPart; /** * The EditPart for a PaletteStack to be used on the toolbar. * * @author Whitney Sorenson * @since 3.0 */ public class PaletteStackEditPart extends PaletteEditPart implements IPaletteStackEditPart { private static final Dimension EMPTY_DIMENSION = new Dimension(0, 0); // listen to changes of clickable tool figure private ChangeListener clickableListener = new ChangeListener() { public void handleStateChanged(ChangeEvent event) { // RAP [am] no mouse over /* * if * (event.getPropertyName().equals(ButtonModel.MOUSEOVER_PROPERTY)) * arrowFigure.getModel().setMouseOver( * activeFigure.getModel().isMouseOver()); else */if (event.getPropertyName().equals(ButtonModel.ARMED_PROPERTY)) arrowFigure.getModel().setArmed( activeFigure.getModel().isArmed()); } }; // listen to changes of arrow figure private ChangeListener clickableArrowListener = new ChangeListener() { public void handleStateChanged(ChangeEvent event) { // RAP [am] no mouse over // if // (event.getPropertyName().equals(ButtonModel.MOUSEOVER_PROPERTY)) // activeFigure.getModel().setMouseOver( // arrowFigure.getModel().isMouseOver()); // RAPEND: [am] if (event.getPropertyName().equals(ButtonModel.ARMED_PROPERTY)) activeFigure.getModel().setArmed( arrowFigure.getModel().isArmed()); } }; // listen to see if arrow is pressed private ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { openMenu(); } }; // listen to see if active tool is changed in palette private PaletteListener paletteListener = new PaletteListener() { public void activeToolChanged(PaletteViewer palette, ToolEntry tool) { if (getStack().getChildren().contains(tool)) { if (!arrowFigure.getModel().isSelected()) arrowFigure.getModel().setSelected(true); if (!getStack().getActiveEntry().equals(tool)) getStack().setActiveEntry(tool); } else arrowFigure.getModel().setSelected(false); } }; private Clickable activeFigure; private RolloverArrow arrowFigure; private Figure contentsFigure; private Menu menu; /** * Creates a new PaletteStackEditPart with the given PaletteStack as its * model. * * @param model * the PaletteStack to associate with this EditPart. */ public PaletteStackEditPart(PaletteStack model) { super(model); } /** * @see org.eclipse.gef.EditPart#activate() */ public void activate() { // in case the model is out of sync checkActiveEntrySync(); getPaletteViewer().addPaletteListener(paletteListener); super.activate(); } /** * Called when the active entry has changed. * * @param oldValue * the old model value (can be null) * @param newValue * the new model value (can be null) */ private void activeEntryChanged(Object oldValue, Object newValue) { GraphicalEditPart part = null; Clickable clickable = null; if (newValue != null) { part = (GraphicalEditPart) getViewer().getEditPartRegistry().get( newValue); clickable = (Clickable) part.getFigure(); clickable.setVisible(true); clickable.addChangeListener(clickableListener); activeFigure = clickable; } else { activeFigure = null; } if (oldValue != null) { part = (GraphicalEditPart) getViewer().getEditPartRegistry().get( oldValue); // if part is null, its no longer a child. if (part != null) { clickable = (Clickable) part.getFigure(); clickable.setVisible(false); clickable.removeChangeListener(clickableListener); } } } private void checkActiveEntrySync() { if (activeFigure == null) activeEntryChanged(null, getStack().getActiveEntry()); } /** * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure() */ public IFigure createFigure() { Figure figure = new Figure() { public Dimension getPreferredSize(int wHint, int hHint) { if (PaletteStackEditPart.this.getChildren().isEmpty()) return EMPTY_DIMENSION; return super.getPreferredSize(wHint, hHint); } }; figure.setLayoutManager(new BorderLayout()); contentsFigure = new Figure(); StackLayout stackLayout = new StackLayout(); // make it so the stack layout does not allow the invisible figures to // contribute // to its bounds stackLayout.setObserveVisibility(true); contentsFigure.setLayoutManager(stackLayout); figure.add(contentsFigure, BorderLayout.CENTER); arrowFigure = new RolloverArrow(); arrowFigure.addChangeListener(clickableArrowListener); arrowFigure.addActionListener(actionListener); figure.add(arrowFigure, BorderLayout.RIGHT); return figure; } /** * @see org.eclipse.gef.EditPart#deactivate() */ public void deactivate() { if (activeFigure != null) activeFigure.removeChangeListener(clickableListener); arrowFigure.removeActionListener(actionListener); arrowFigure.removeChangeListener(clickableArrowListener); getPaletteViewer().removePaletteListener(paletteListener); super.deactivate(); } /** * @see org.eclipse.gef.EditPart#eraseTargetFeedback(org.eclipse.gef.Request) */ public void eraseTargetFeedback(Request request) { Iterator children = getChildren().iterator(); while (children.hasNext()) { PaletteEditPart part = (PaletteEditPart) children.next(); part.eraseTargetFeedback(request); } super.eraseTargetFeedback(request); } /** * @see org.eclipse.gef.GraphicalEditPart#getContentPane() */ public IFigure getContentPane() { return contentsFigure; } private PaletteStack getStack() { return (PaletteStack) getModel(); } /** * Opens the menu to display the choices for the active entry. */ public void openMenu() { MenuManager menuManager = new MenuManager(); Iterator children = getChildren().iterator(); PaletteEditPart part = null; PaletteEntry entry = null; while (children.hasNext()) { part = (PaletteEditPart) children.next(); entry = (PaletteEntry) part.getModel(); menuManager .add(new SetActivePaletteToolAction(getPaletteViewer(), entry.getLabel(), entry.getSmallIcon(), getStack() .getActiveEntry().equals(entry), (ToolEntry) entry)); } menu = menuManager.createContextMenu(getPaletteViewer().getControl()); // make the menu open below the figure Rectangle figureBounds = getFigure().getBounds().getCopy(); getFigure().translateToAbsolute(figureBounds); Point menuLocation = getPaletteViewer().getControl().toDisplay( figureBounds.getBottomLeft().x, figureBounds.getBottomLeft().y); // remove feedback from the arrow Figure and children figures arrowFigure.getModel().setMouseOver(false); eraseTargetFeedback(new Request(RequestConstants.REQ_SELECTION)); menu.setLocation(menuLocation); menu.addMenuListener(new StackMenuListener(menu, getViewer() .getControl().getDisplay())); menu.setVisible(true); } /** * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName().equals(PaletteStack.PROPERTY_ACTIVE_ENTRY)) activeEntryChanged(event.getOldValue(), event.getNewValue()); else super.propertyChange(event); } /** * @see org.eclipse.gef.editparts.AbstractEditPart#refreshChildren() */ protected void refreshChildren() { super.refreshChildren(); checkActiveEntrySync(); Iterator children = getChildren().iterator(); while (children.hasNext()) { PaletteEditPart editPart = (PaletteEditPart) children.next(); if (!editPart.getFigure().equals(activeFigure)) editPart.getFigure().setVisible(false); } } /** * @see org.eclipse.gef.EditPart#showTargetFeedback(org.eclipse.gef.Request) */ public void showTargetFeedback(Request request) { // if menu is showing, don't show feedback. this is a fix // for the occasion when show is called after forced erase if (menu != null && !menu.isDisposed() && menu.isVisible()) return; Iterator children = getChildren().iterator(); while (children.hasNext()) { PaletteEditPart part = (PaletteEditPart) children.next(); part.showTargetFeedback(request); } super.showTargetFeedback(request); } public PaletteEditPart getActiveEntry() { return (PaletteEditPart) getViewer().getEditPartRegistry().get( getStack().getActiveEntry()); } } class StackMenuListener implements MenuListener { private Menu menu; private Display d; /** * Creates a new listener to listen to the menu that it used to select the * active tool on a stack. Disposes the stack with an asyncExec after hidden * is called. */ StackMenuListener(Menu menu, Display d) { this.menu = menu; this.d = d; } /** * @see org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events.MenuEvent) */ public void menuShown(MenuEvent e) { } /** * @see org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events.MenuEvent) */ public void menuHidden(MenuEvent e) { d.asyncExec(new Runnable() { public void run() { if (menu != null) { if (!menu.isDisposed()) menu.dispose(); menu = null; } } }); } } class RolloverArrow extends Clickable { private final Border BORDER_TOGGLE = new ButtonBorder( ButtonBorder.SCHEMES.TOOLBAR()); /** * Creates a new Clickable that paints a triangle figure. */ RolloverArrow() { super(); setRolloverEnabled(true); setBorder(BORDER_TOGGLE); setBackgroundColor(ColorConstants.black()); setOpaque(false); setPreferredSize(11, -1); } /** * @return false so that the focus rectangle is not drawn. */ public boolean hasFocus() { return false; } public void paintFigure(Graphics graphics) { Rectangle rect = getClientArea(); graphics.translate(getLocation()); // fill the arrow int[] points = new int[8]; points[0] = 3; points[1] = rect.height / 2; points[2] = 8; points[3] = rect.height / 2; points[4] = 5; points[5] = 3 + rect.height / 2; points[6] = 3; points[7] = rect.height / 2; graphics.fillPolygon(points); graphics.translate(getLocation().getNegated()); } }