/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.utils.ui.color; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ColorCellEditor; import org.eclipse.jface.viewers.ICellEditorListener; import org.eclipse.jface.viewers.ICellModifier; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.ColorDialog; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; /** * Widget to create and edit color gradients * * @author Marko Luukkainen * */ public class ColorGradientComposite extends Composite implements ISelectionChangedListener, ICellEditorListener, IPropertyChangeListener{ private static int ICON_WIDTH = 36; private static int ICON_HEIGHT = 14; private static final String COLOR_ID = "Color"; private static final String VALUE_ID = "Value"; private ColorGradientCanvas gradientComposite; private TableViewer viewer; private Button addButton; private Button editButton; private Button removeButton; private Button rgbButton; private Button hsvButton; private int type = ColorGradient.RGB; private ArrayList values = new ArrayList(); private HashMap images = new HashMap(); public ColorGradientComposite(Composite parent, int style) { super(parent,style); GridLayout layout = new GridLayout(2,false); this.setLayout(layout); gradientComposite = new ColorGradientCanvas(this,SWT.HORIZONTAL); // Group typeComposite = new Group(this,SWT.NONE); // typeComposite.setText("Interpolation"); Composite typeComposite = new Composite(this, SWT.NONE); typeComposite.setLayout(new GridLayout(1,false)); rgbButton = new Button(typeComposite,SWT.RADIO); rgbButton.setSelection(true); rgbButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } public void widgetSelected(SelectionEvent e) { rgbButton.setSelection(true); hsvButton.setSelection(false); type = ColorGradient.RGB; gradientComposite.setGradient(new ColorGradient(values,type)); } }); rgbButton.setText("RGB"); hsvButton = new Button(typeComposite,SWT.RADIO); hsvButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } public void widgetSelected(SelectionEvent e) { hsvButton.setSelection(true); rgbButton.setSelection(false); type = ColorGradient.HSV; gradientComposite.setGradient(new ColorGradient(values,type)); } }); hsvButton.setText("HSV"); viewer = new TableViewer(this,SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION); final Table table = viewer.getTable(); table.setLinesVisible(true); table.setHeaderVisible(true); viewer.addSelectionChangedListener(this); viewer.addPostSelectionChangedListener(this); TableColumn column = new TableColumn(table,SWT.RIGHT); column.setText("Color"); column.setWidth(60); column.setMoveable(false); column.setResizable(false); column = new TableColumn(table,SWT.LEFT); column.setText("Value"); column.setWidth(300); column.setMoveable(false); viewer.setColumnProperties(new String[]{COLOR_ID,VALUE_ID}); viewer.setLabelProvider(new TableLabelProvider()); viewer.setContentProvider(new TableContentProvider()); viewer.setInput(values); final ColorCellEditor colorEditor = new ColorCellEditor(table); final TextCellEditor valueEditor = new TextCellEditor(table); colorEditor.addListener(this); valueEditor.addListener(this); valueEditor.addPropertyChangeListener(this); viewer.setCellEditors(new CellEditor[]{colorEditor,valueEditor}); viewer.setCellModifier(new TableCellEditorModifier()); Composite buttonComposite = new Composite(this,SWT.NONE); buttonComposite.setLayout(new FillLayout(SWT.VERTICAL)); addButton = new Button(buttonComposite,SWT.PUSH); addButton.setText("Add new color"); addButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() { public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { ColorDialog dialog = new ColorDialog(ColorGradientComposite.this.getShell(), SWT.NONE); RGB rgb = dialog.open(); if (rgb != null) { Color c = new Color(rgb.red,rgb.green,rgb.blue); Double value; if (values.size() == 0) { value = 0.0; } else if (values.size() == 1) { value = 100.0; } else { StructuredSelection selection = (StructuredSelection)viewer.getSelection(); if (selection.size() == 1) { // add new color next to the selection ColorValue v = (ColorValue)selection.getFirstElement(); int index = values.indexOf(v); if (index == values.size() -1) { index--; } value = (values.get(index+1).getValue()-values.get(index).getValue())*0.5; value += values.get(index).getValue(); } else { // add new color to largest gap int index = 0; double r = 0.0; for (int i = 0; i < values.size() -1; i++) { double v1 = values.get(i).getValue(); double v2 = values.get(i+1).getValue(); double vr = v2 -v1; if (vr > r) { r=vr; index = i; } } value = values.get(index).getValue() + r *0.5; } } addColor(c,value); } } }); editButton = new Button(buttonComposite,SWT.PUSH); editButton.setText("Edit color"); editButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() { public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { TableItem selection[] = table.getSelection(); if (selection.length > 0) { TableItem selected = selection[0]; Object obj = selected.getData(); if (obj instanceof ColorValue) { ColorDialog dialog = new ColorDialog(ColorGradientComposite.this.getShell(), SWT.NONE); dialog.setRGB(getRGB(((ColorValue)obj).getColor())); RGB rgb = dialog.open(); if (rgb != null) { modifyColorValueColor((ColorValue)obj,rgb); } } } } }); editButton.setEnabled(false); removeButton = new Button(buttonComposite,SWT.PUSH); removeButton.setText("Remove color"); removeButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() { public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { TableItem selection[] = table.getSelection(); if (selection.length > 0) { TableItem selected = selection[0]; Object o = selected.getData(); if (o instanceof ColorValue) { Image i = images.get(o); i.dispose(); images.remove(o); values.remove(o); updateWidgets(); } } } }); removeButton.setEnabled(false); GridDataFactory.fillDefaults().span(1, 1).grab(true, false).align(SWT.FILL, SWT.CENTER).hint(SWT.DEFAULT, 32).applyTo(gradientComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).hint(SWT.DEFAULT, 100).applyTo(table); GridDataFactory.fillDefaults().grab(false, true).align(SWT.LEFT, SWT.FILL).applyTo(typeComposite); GridDataFactory.fillDefaults().span(1, 1).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(buttonComposite); } public void addColor(Color color, double value) { addColor(new ColorValue(color,value)); } public void addColor(ColorValue value) { values.add(value); Image image = ColorIconCreator.createImage(value.getColor(), ICON_WIDTH, ICON_HEIGHT, SWT.BORDER); images.put(value,image); updateWidgets(); //viewer.refresh(true); } private void updateWidgets() { Collections.sort(values,new ColorValueComparator()); gradientComposite.setGradient(new ColorGradient(values,type)); viewer.refresh(); } public ColorGradient getGradient() { return new ColorGradient(values,type); } public void dispose() { for (Image i : images.values()) i.dispose(); } public void setGradient(ColorGradient gradient) { for (Image i : images.values()) i.dispose(); values.clear(); images.clear(); type = gradient.getType(); for (ColorValue value : gradient.getColorValues()) addColor(value); if (type == ColorGradient.HSV) { rgbButton.setSelection(false); hsvButton.setSelection(true); } else if (type == ColorGradient.RGB) { hsvButton.setSelection(false); rgbButton.setSelection(true); } } private RGB getRGB(Color color) { RGB rgb = new RGB(color.getR(),color.getG(),color.getB()); return rgb; } private void modifyColorValueValue(ColorValue cValue, String string) { try { double d = Double.parseDouble(string); values.remove(cValue); Image image = images.get(cValue); images.remove(cValue); ColorValue newCValue = new ColorValue(cValue.getColor(),d); values.add(newCValue); images.put(newCValue,image); updateWidgets(); } catch (NumberFormatException e) { return; } } private void modifyColorValueColor(ColorValue cValue, RGB rgb) { values.remove(cValue); Image oldImage = images.get(cValue); oldImage.dispose(); images.remove(cValue); Color newColor = new Color(rgb.red,rgb.green,rgb.blue); ColorValue newCValue = new ColorValue(newColor,cValue.getValue()); values.add(newCValue); Image newImage = ColorIconCreator.createImage(newColor, ICON_WIDTH, ICON_HEIGHT, SWT.BORDER); images.put(newCValue,newImage); updateWidgets(); } /** * Enables and disables "Edit color" and "Remove color" buttons depending on selected item */ public void selectionChanged(SelectionChangedEvent event) { Object obj = ((IStructuredSelection)event.getSelection()).getFirstElement(); if (obj == null) { editButton.setEnabled(false); removeButton.setEnabled(false); } else { editButton.setEnabled(true); removeButton.setEnabled(true); } } /* * FIXME : * If item is opened for editing selectionChanged won't get null selection and * "Edit" & "Remove" buttons are enabled when there's no valid selection * * There seems to be no way to get event when cellEditor is activated... */ public void applyEditorValue() { // TODO Auto-generated method stub editButton.setEnabled(false); removeButton.setEnabled(false); } public void cancelEditor() { // TODO Auto-generated method stub editButton.setEnabled(false); removeButton.setEnabled(false); } public void editorValueChanged(boolean oldValidState, boolean newValidState) { // TODO Auto-generated method stub editButton.setEnabled(false); removeButton.setEnabled(false); } public void propertyChange(PropertyChangeEvent event) { editButton.setEnabled(false); removeButton.setEnabled(false); } /** * @author Marko Luukkainen * */ private class TableLabelProvider implements ITableLabelProvider { private ArrayList listeners = new ArrayList(); /* (non-Javadoc) * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() */ public void dispose() { } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) */ public Image getColumnImage(Object element, int columnIndex) { //System.out.println("TableLabelProvider.getColumnImage() " + element + " , " + columnIndex); ColorValue value = (ColorValue)element; if (columnIndex == 0) return images.get(value); return null; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) */ public String getColumnText(Object element, int columnIndex) { //System.out.println("TableLabelProvider.getColumnText() " + element + " , " + columnIndex); ColorValue value = (ColorValue)element; if (columnIndex == 1) return Double.toString(value.getValue()); return null; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String) */ public boolean isLabelProperty(Object element, String property) { return true; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) */ public void addListener(ILabelProviderListener listener) { if(!listeners.contains(listener)) listeners.add(listener); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) */ public void removeListener(ILabelProviderListener listener) { listeners.remove(listener); } } /** * @author Marko Luukkainen * */ private class TableContentProvider implements IStructuredContentProvider { public void dispose() { } public Object[] getElements(Object inputElement) { ColorValue valueArray[] = new ColorValue[values.size()]; // values.toArray(valueArray); // to get correct ordering (smallest last) // we must reverse the order of the list for (int i = 0; i < values.size(); i++) { valueArray[i] = values.get(values.size() - 1 -i); } //System.out.println("TableContentProvider.getElements() : array size " + valueArray.length); return valueArray; } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { //System.out.println("TableContentProvider.inputChanged()"); } } /** * @author Marko Luukkainen * */ private class TableCellEditorModifier implements ICellModifier { public boolean canModify(Object element, String property) { //System.out.println("TableCellEditorModifier.canModify()" + element + " , " + property); if (property == VALUE_ID) return true; // this is commented out because it's hard so select the row when both rows have editors // now the color can be changed with "Edit Color" button //if (property == COLOR_ID) // return true; return false; } public Object getValue(Object element, String property) { //System.out.println("TableCellEditorModifier.getValue()" + element + " , " + property); if (property == VALUE_ID) return Double.toString(((ColorValue)element).getValue()); if (property == COLOR_ID) return getRGB(((ColorValue)element).getColor()); return null; } public void modify(Object element, String property, Object value) { TableItem item = (TableItem)element; Object obj = item.getData(); //System.out.println("TableCellEditorModifier.modify()" + element + " , " + property + " , " + value); if (property == VALUE_ID) modifyColorValueValue((ColorValue)obj,(String)value); if (property == COLOR_ID) modifyColorValueColor((ColorValue)obj,(RGB)value); } } }