/****************************************************************************** * Copyright (c) 2002, 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.gmf.runtime.diagram.ui.actions.internal; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.gef.EditPart; import org.eclipse.gmf.runtime.common.ui.util.WindowUtil; import org.eclipse.gmf.runtime.diagram.ui.actions.ActionIds; import org.eclipse.gmf.runtime.diagram.ui.actions.internal.l10n.DiagramUIActionsMessages; import org.eclipse.gmf.runtime.diagram.ui.actions.internal.l10n.DiagramUIActionsPluginImages; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.internal.properties.Properties; import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; import org.eclipse.jface.resource.CompositeImageDescriptor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.ColorDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IWorkbenchPage; /** * @author melaasar */ public class ColorPropertyContributionItem extends PropertyChangeContributionItem implements Listener { /** * An image descriptor that overlays two images: a basic icon * and a thin color bar underneath it */ private static class ColorMenuImageDescriptor extends CompositeImageDescriptor { /** the basic icon */ private ImageData basicImgData; /** the color of the thin color bar */ private RGB rgb; /** * Creates a new color menu image descriptor * @param basicIcon The basic Image data * @param rgb The color bar RGB value */ public ColorMenuImageDescriptor(ImageData basicImgData, RGB rgb) { this.basicImgData = basicImgData; this.rgb = rgb; } /** * @see org.eclipse.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, int) */ protected void drawCompositeImage(int width, int height) { // draw the base image drawImage(basicImgData, 0, 0); // draw the thin color bar underneath if (rgb != null) { ImageData colorBar = new ImageData(14, 4, 1, new PaletteData(new RGB[] { rgb })); drawImage(colorBar, 1, height - 4); } } /** * @see org.eclipse.jface.resource.CompositeImageDescriptor#getSize() */ protected Point getSize() { return ICON_SIZE; } } /** * An image descriptor that creates a box with a given color */ private static class ColorBoxImageDescriptor extends ImageDescriptor { /** the color value in RGB scheme */ private RGB rgb; /** * Creates a new instance of the color box image descriptor with * a given color * * @param rgb The color value in RGB scheme */ public ColorBoxImageDescriptor(RGB rgb) { this.rgb = rgb; } /** * @see org.eclipse.jface.resource.ImageDescriptor#getImageData() */ public ImageData getImageData() { ImageData data = new ImageData( ICON_SIZE.x, ICON_SIZE.y, 1, new PaletteData(new RGB[] { rgb, OUTLINE_COLOR })); for (int i = 0; i < ICON_SIZE.y; i++) data.setPixel(0, i, 1); for (int i = 0; i < ICON_SIZE.y; i++) data.setPixel(ICON_SIZE.x - 1, i, 1); for (int i = 0; i < ICON_SIZE.x; i++) data.setPixel(i, 0, 1); for (int i = 0; i < ICON_SIZE.x; i++) data.setPixel(i, ICON_SIZE.y - 1, 1); return data; } } /** * A descirptor for an inventory color */ private static class InventoryColorDescriptor { public RGB colorValue; public String colorName; public InventoryColorDescriptor(RGB colorValue, String colorName) { this.colorValue = colorValue; this.colorName = colorName; } } /** inventory colors */ private static final InventoryColorDescriptor WHITE = new InventoryColorDescriptor(new RGB(255, 255, 255), DiagramUIActionsMessages.ColorPropertyChangeAction_white); private static final InventoryColorDescriptor BLACK = new InventoryColorDescriptor(new RGB(0, 0, 0), DiagramUIActionsMessages.ColorPropertyChangeAction_black); private static final InventoryColorDescriptor LIGHT_GRAY = new InventoryColorDescriptor(new RGB(192, 192, 192), DiagramUIActionsMessages.ColorPropertyChangeAction_lightGray); private static final InventoryColorDescriptor GRAY = new InventoryColorDescriptor(new RGB(128, 128, 128), DiagramUIActionsMessages.ColorPropertyChangeAction_gray); private static final InventoryColorDescriptor DARK_GRAY = new InventoryColorDescriptor(new RGB(64, 64, 64), DiagramUIActionsMessages.ColorPropertyChangeAction_darkGray); private static final InventoryColorDescriptor RED = new InventoryColorDescriptor(new RGB(227, 164, 156), DiagramUIActionsMessages.ColorPropertyChangeAction_red); private static final InventoryColorDescriptor GREEN = new InventoryColorDescriptor(new RGB(166, 193, 152), DiagramUIActionsMessages.ColorPropertyChangeAction_green); private static final InventoryColorDescriptor BLUE = new InventoryColorDescriptor(new RGB(152, 168, 191), DiagramUIActionsMessages.ColorPropertyChangeAction_blue); private static final InventoryColorDescriptor YELLOW = new InventoryColorDescriptor(new RGB(225, 225, 135), DiagramUIActionsMessages.ColorPropertyChangeAction_yellow); private static final InventoryColorDescriptor PURPLE = new InventoryColorDescriptor(new RGB(184, 151, 192), DiagramUIActionsMessages.ColorPropertyChangeAction_magenta); private static final InventoryColorDescriptor TEAL = new InventoryColorDescriptor(new RGB(155, 199, 204), DiagramUIActionsMessages.ColorPropertyChangeAction_cyan); private static final InventoryColorDescriptor PINK = new InventoryColorDescriptor(new RGB(228, 179, 229), DiagramUIActionsMessages.ColorPropertyChangeAction_pink); private static final InventoryColorDescriptor ORANGE = new InventoryColorDescriptor(new RGB(237, 201, 122), DiagramUIActionsMessages.ColorPropertyChangeAction_orange); /** default color icon width */ private static final Point ICON_SIZE = new Point(16, 16); /** custom color group maximum length */ private static final int CUSTOM_SIZE = 3; /** the default preference color */ private static final RGB DEFAULT_PREF_COLOR = new RGB(0, 0, 0); /** the default preference color */ private static final RGB OUTLINE_COLOR = new RGB(192, 192, 192); /** a color value that indicates the default color */ private static final String DEFAULT = "Default"; //$NON-NLS-1$ /** a color value that indicates to browse for a color */ private static final String CHOOSE = "Choose"; //$NON-NLS-1$ /** a color value that indicates to browse for a color */ private static final String CLEAR = "Clear"; //$NON-NLS-1$ /** the basic image data */ private ImageData basicImageData; /** the disabledbasic image data */ private ImageData disabledBasicImageData; /** the disabled basic image data **/ private Image disabledBasicImage; /** the overlayed image */ private Image overlyedImage; /** the last selected color */ private Integer lastColor; /** the custom color list */ private List customColors = new ArrayList(); /** the inventory color list */ private List inventoryColors = new ArrayList(); /** the inventory color list key: anRGB, value: anImage */ private HashMap imageColorMap = new HashMap(); /** the drop down menu */ private Menu menu; /** * Creates a new color property contribution item * * @param workbenchPage The part service * @param id The item id * @param propertyName The color property name (externalizable) * @param propertyId The color property id * @param toolTipText The tool tip text * @param basicImageData The basic image data */ public ColorPropertyContributionItem( IWorkbenchPage workbenchPage, String id, String propertyId, String propertyName, String toolTipText, ImageData basicImageData, ImageData disabledBasicImageData) { super(workbenchPage, id, propertyId, propertyName); assert null != toolTipText; assert null != basicImageData; this.basicImageData = basicImageData; this.disabledBasicImageData = disabledBasicImageData; setLabel(toolTipText); } /** * @see org.eclipse.gmf.runtime.common.ui.action.AbstractContributionItem#init() */ protected void init() { super.init(); this.overlyedImage = new ColorMenuImageDescriptor(getBasicImageData(), null).createImage(); this.disabledBasicImage = new ColorMenuImageDescriptor(this.disabledBasicImageData, null).createImage(); } /** * @see org.eclipse.jface.action.IContributionItem#dispose() */ public void dispose() { if (overlyedImage != null && !overlyedImage.isDisposed()){ overlyedImage.dispose(); overlyedImage = null; } if (menu != null && !menu.isDisposed()){ menu.dispose(); menu = null; } for (Iterator i = imageColorMap.values().iterator(); i.hasNext();) { Image image = (Image) i.next(); if (! image.isDisposed()) { image.dispose(); } } if (disabledBasicImage != null && !disabledBasicImage.isDisposed()){ disabledBasicImage.dispose(); disabledBasicImage = null; } imageColorMap = new HashMap(); super.dispose(); } /** * Render the UI as a * @see org.eclipse.gmf.runtime.common.ui.action.AbstractContributionItem#createToolItem(org.eclipse.swt.widgets.ToolBar, int) */ protected ToolItem createToolItem(ToolBar parent, int index) { ToolItem ti = new ToolItem(parent, SWT.DROP_DOWN, index); ti.addListener(SWT.Selection, getItemListener()); ti.setImage(overlyedImage); ti.setDisabledImage(this.disabledBasicImage); return ti; } /** * @see org.eclipse.gmf.runtime.common.ui.action.AbstractContributionItem#createMenuItem(org.eclipse.swt.widgets.Menu, int) */ protected MenuItem createMenuItem(Menu parent, int index) { MenuItem mi = index >= 0 ? new MenuItem(parent, SWT.CASCADE, index) : new MenuItem(parent, SWT.CASCADE); createMenu(mi); mi.setImage(overlyedImage); return mi; } /** * @see org.eclipse.gmf.runtime.common.ui.action.AbstractContributionItem#handleWidgetEvent(org.eclipse.swt.widgets.Event) */ protected void handleWidgetEvent(Event e) { switch (e.type) { case SWT.Selection : handleWidgetSelection(e); break; default : super.handleWidgetEvent(e); } } /** * Handles a widget selection event. */ private void handleWidgetSelection(Event e) { if (e.detail == 4) { // on drop-down button createMenu(getItem()); } else { // on icon button if (lastColor != null) runWithEvent(e); } } /** * Creates the color menu */ private void createMenu(Item item) { if (menu != null && !menu.isDisposed()) menu.dispose(); if (item instanceof ToolItem) { ToolItem toolItem = (ToolItem) item; menu = new Menu(toolItem.getParent()); Rectangle b = toolItem.getBounds(); Point p = toolItem.getParent().toDisplay(new Point(b.x, b.y + b.height)); menu.setLocation(p.x, p.y); // waiting for SWT 0.42 menu.setVisible(true); } else if (item instanceof MenuItem) { MenuItem menuItem = (MenuItem) item; menu = new Menu(menuItem.getParent()); menuItem.setMenu(menu); } assert null != menu : "falid to create menu"; //$NON-NLS-1$ buildMenu(menu); } /** * @see org.eclipse.gmf.runtime.diagram.ui.actions.internal.PropertyChangeContributionItem#getNewPropertyValue() */ protected Object getNewPropertyValue() { return lastColor; } /** * Builds the color menu consisting of : * inventory colors, custom colors, default and color picker * * @param theMenu The menu */ private void buildMenu(Menu theMenu) { // inventory colors createInventoryColorMenuItem(theMenu, WHITE); createInventoryColorMenuItem(theMenu, BLACK); createInventoryColorMenuItem(theMenu, LIGHT_GRAY); createInventoryColorMenuItem(theMenu, GRAY); createInventoryColorMenuItem(theMenu, DARK_GRAY); createInventoryColorMenuItem(theMenu, RED); createInventoryColorMenuItem(theMenu, GREEN); createInventoryColorMenuItem(theMenu, BLUE); createInventoryColorMenuItem(theMenu, YELLOW); createInventoryColorMenuItem(theMenu, PURPLE); createInventoryColorMenuItem(theMenu, TEAL); createInventoryColorMenuItem(theMenu, PINK); createInventoryColorMenuItem(theMenu, ORANGE); // history colors if (!customColors.isEmpty()) { createMenuSeparator(theMenu); Iterator iter = customColors.iterator(); while (iter.hasNext()) { RGB rgb = (RGB) iter.next(); createColorMenuItem(theMenu, rgb); } createClearCustomColorMenuItem(theMenu); } // default color and color picker createMenuSeparator(theMenu); createDefaultColorMenuItem(theMenu); createChooseColorMenuItem(theMenu); } /** * Creates a new menu separator * * @param theMenu The menu */ private void createMenuSeparator(Menu theMenu) { new MenuItem(theMenu, SWT.SEPARATOR); } /** * Creates a inventory color menu item with the given color name and RGB value * * @param theMenu The menu * @param color The color RGB value * @param colorName the color name (externalizable) */ private void createInventoryColorMenuItem( Menu theMenu, InventoryColorDescriptor color) { RGB rgb = color.colorValue; Image image = (Image) imageColorMap.get(rgb); if (image == null){ image = new ColorBoxImageDescriptor(color.colorValue).createImage(); imageColorMap.put(rgb, image); } MenuItem mi = createMenuItem(theMenu, color.colorName, image); mi.setData(rgb); inventoryColors.add(rgb); } /** * Creates a color menu item with the RGB value as a name * * @param theMenu The menu * @param rgb The color RGB value */ private void createColorMenuItem(Menu theMenu, RGB rgb) { Image image = (Image) imageColorMap.get(rgb); if (image == null){ image = new ColorBoxImageDescriptor(rgb).createImage(); imageColorMap.put(rgb, image); } MenuItem mi = createMenuItem(theMenu, rgb.toString(), image); mi.setData(rgb); } /** * Creates a menu item for the default color * * @param theMenu The menu */ private void createDefaultColorMenuItem(Menu theMenu) { String text = DiagramUIActionsMessages.ColorPropertyChangeAction_default; Image image = null; //new ColorBoxImageDescriptor(color).createImage(); MenuItem mi = createMenuItem(theMenu, text, image); mi.setData(DEFAULT); } /** * Creates a menu item for the color picker * * @param theMenu The menu */ private void createChooseColorMenuItem(Menu theMenu) { String text = DiagramUIActionsMessages.ColorPropertyChangeAction_moreColors; Image image = null; //new ColorBoxImageDescriptor(color).createImage(); MenuItem mi = createMenuItem(theMenu, text, image); mi.setData(CHOOSE); } /** * Creates a menu item to clear the custom color menu group * * @param theMenu The menu */ private void createClearCustomColorMenuItem(Menu theMenu) { String text = DiagramUIActionsMessages.ColorPropertyChangeAction_clearColors; Image image = null; //new ColorBoxImageDescriptor(color).createImage(); MenuItem mi = createMenuItem(theMenu, text, image); mi.setData(CLEAR); } /** * Creates a menu item with a given text and image with the push style * * @param theMenu The menu * @param text The menu item text * @param image The menu item image */ private MenuItem createMenuItem(Menu theMenu, String text, Image image) { MenuItem mi = new MenuItem(theMenu, SWT.PUSH); if (text != null) mi.setText(text); if (image != null) mi.setImage(image); mi.addListener(SWT.Selection, this); return mi; } /** * Handle the selection of menu items in the color menu * * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) */ public void handleEvent(Event event) { MenuItem menuItem = (MenuItem) event.widget; Object data = menuItem.getData(); RGB rgb = null; if (data instanceof RGB) { rgb = (RGB) data; } else if (data.equals(CHOOSE)) { rgb = getBrowseColor(); } else if (data.equals(DEFAULT)) { rgb = getDefaultColor(); } else if (data.equals(CLEAR)) { customColors.clear(); } if (rgb != null) { if (getToolItem() != null) { // if a new custom color add it to history if (!customColors.contains(rgb) && !inventoryColors.contains(rgb)) { if (customColors.size() == CUSTOM_SIZE) customColors.remove(0); customColors.add(rgb); } // create a new overlayed icon with the new color if (overlyedImage != null) overlyedImage.dispose(); overlyedImage = new ColorMenuImageDescriptor( getBasicImageData(), rgb) .createImage(); getItem().setImage(overlyedImage); } // set the new color as the last color lastColor = FigureUtilities.RGBToInteger(rgb); // run the action runWithEvent(event); } } /** * Returns the color to use in the browse mode. By default, * this comes from the color picker dialog * * @return The color to use in browse mode */ protected RGB getBrowseColor() { ColorDialog dialog = new ColorDialog(Display.getCurrent().getActiveShell()); WindowUtil.centerDialog( dialog.getParent(), Display.getCurrent().getActiveShell()); if (lastColor != null){ dialog.setRGB(FigureUtilities.integerToRGB(lastColor)); } dialog.open(); return dialog.getRGB(); } /** * Returns the color to use in the default mode. A limitation is that if * there are multiple editparts with different default colors only the * default color of the first is returned. * * @return The color to use in default mode */ protected RGB getDefaultColor() { for (Iterator iter = getOperationSet().iterator(); iter.hasNext();) { EditPart editpart = (EditPart) iter.next(); if (editpart instanceof IGraphicalEditPart) { final EStructuralFeature feature = (EStructuralFeature) PackageUtil .getElement(getPropertyId()); Object preferredValue = ((IGraphicalEditPart) editpart) .getPreferredValue(feature); if (preferredValue instanceof Integer) { return FigureUtilities .integerToRGB((Integer) preferredValue); } } } return DEFAULT_PREF_COLOR; } /** * Gets the basic image data. * @return ImageData basicImageData */ protected ImageData getBasicImageData(){ return this.basicImageData; } /** * Create the FONT color menu * * @param workbenchPage The part service * @return The FONT color menu */ public static ColorPropertyContributionItem createFontColorContributionItem(IWorkbenchPage workbenchPage) { String propertyName = DiagramUIActionsMessages.PropertyDescriptorFactory_FontColor; String toolTipText = DiagramUIActionsMessages.ColorChangeActionMenu_fontColor; ImageData basicImageData = DiagramUIActionsPluginImages.DESC_FONT_COLOR .getImageData(); ImageData disabledBasicImageData = DiagramUIActionsPluginImages.DESC_FONT_COLOR_DISABLED .getImageData(); return new ColorPropertyContributionItem( workbenchPage, ActionIds.CUSTOM_FONT_COLOR, Properties.ID_FONTCOLOR, propertyName, toolTipText, basicImageData, disabledBasicImageData); } /** * Create the LINE color menu * * @param workbenchPage The part service * @return The LINE color menu */ public static ColorPropertyContributionItem createLineColorContributionItem(IWorkbenchPage workbenchPage) { String propertyName = DiagramUIActionsMessages.PropertyDescriptorFactory_LineColor; String toolTipText = DiagramUIActionsMessages.ColorChangeActionMenu_lineColor; ImageData basicImageData = DiagramUIActionsPluginImages.DESC_LINE_COLOR.getImageData(); ImageData disabledBasicImageData = DiagramUIActionsPluginImages.DESC_LINE_COLOR_DISABLED .getImageData(); return new ColorPropertyContributionItem( workbenchPage, ActionIds.CUSTOM_LINE_COLOR, Properties.ID_LINECOLOR, propertyName, toolTipText, basicImageData, disabledBasicImageData); } /** * Create the FILL color menu * * @param workbenchPage The part service * @return The FILL color menu */ public static ColorPropertyContributionItem createFillColorContributionItem(IWorkbenchPage workbenchPage) { String propertyName = DiagramUIActionsMessages.PropertyDescriptorFactory_FillColor; String toolTipText = DiagramUIActionsMessages.ColorChangeActionMenu_fillColor; ImageData basicImageData = DiagramUIActionsPluginImages.DESC_FILL_COLOR.getImageData(); ImageData disabledBasicImageData = DiagramUIActionsPluginImages.DESC_FILL_COLOR_DISABLED .getImageData(); return new ColorPropertyContributionItem( workbenchPage, ActionIds.CUSTOM_FILL_COLOR, Properties.ID_FILLCOLOR, propertyName, toolTipText, basicImageData, disabledBasicImageData); } @Override protected boolean digIntoGroups() { return true; } }