From: Tuukka Lehtonen Date: Fri, 30 Sep 2016 07:59:43 +0000 (+0300) Subject: Sync git svn branch with SVN repository r33269. X-Git-Tag: v1.25.0~80^2 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=bf75fd9740858140eac90c18f0bca0aea3893248 Sync git svn branch with SVN repository r33269. refs #6724 refs #6723 refs #6704 refs #6495 --- diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/Charts.java b/bundles/org.simantics.charts/src/org/simantics/charts/Charts.java index dd7ae02a0..1e503159e 100644 --- a/bundles/org.simantics.charts/src/org/simantics/charts/Charts.java +++ b/bundles/org.simantics.charts/src/org/simantics/charts/Charts.java @@ -3,7 +3,6 @@ package org.simantics.charts; import java.util.Collections; import java.util.List; -import org.simantics.Simantics; import org.simantics.charts.editor.ChartData; import org.simantics.charts.editor.ChartKeys; import org.simantics.databoard.binding.error.BindingException; @@ -12,6 +11,7 @@ import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; import org.simantics.history.HistoryException; import org.simantics.history.HistorySamplerItem; import org.simantics.history.ItemManager; @@ -19,7 +19,7 @@ import org.simantics.history.util.subscription.SamplingFormat; import org.simantics.modeling.subscription.SubscriptionItem; import org.simantics.modeling.subscription.SubscriptionItemQuery; import org.simantics.project.IProject; -import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.simulation.experiment.IExperiment; /** * Main facade for externally dealing with the trending system. @@ -38,40 +38,38 @@ public final class Charts { } } - public static HistorySamplerItem createHistorySamplerItem(ReadGraph graph, Resource subscriptionItem) throws DatabaseException { - - try { - - Resource model = graph.syncRequest(new PossibleIndexRoot(subscriptionItem)); - if ( model == null ) { - throw new DatabaseException("There is no model for " + subscriptionItem); - } - - Key chartDataKey = ChartKeys.chartSourceKey(model); + public static HistorySamplerItem createHistorySamplerItem(ReadGraph graph, Variable run, Resource subscriptionItem) throws DatabaseException { + IExperiment exp = (IExperiment) run.getPropertyValue(graph, "iExperiment"); + ITrendSupport support = exp.getService(ITrendSupport.class); + ChartData data = support.getChartData(); + return createHistorySamplerItem(graph, subscriptionItem, data); + } - final ChartData data = Simantics.getProject().getHint(chartDataKey); - if ( data == null ) { - throw new DatabaseException("There is no chart data for " + model); - } + public static HistorySamplerItem createHistorySamplerItem(ReadGraph graph, Resource subscriptionItem, ChartData data) throws DatabaseException { - ItemManager im = new ItemManager( data.history.getItems() ); + try { + Resource model = graph.syncRequest(new PossibleIndexRoot(subscriptionItem)); + if (model == null) { + throw new DatabaseException("There is no model for " + subscriptionItem); + } - SubscriptionItem i = graph.syncRequest(new SubscriptionItemQuery(subscriptionItem)); + ItemManager im = new ItemManager(data.history.getItems()); - List items = im.search("variableId", i.variableId); - Collections.sort(items, SamplingFormat.INTERVAL_COMPARATOR); - if (items.isEmpty()) new DatabaseException("There is history item for " + i.variableId); - Bean config = items.get(0); - String historyId = (String) config.getFieldUnchecked("id"); + SubscriptionItem i = graph.syncRequest(new SubscriptionItemQuery(subscriptionItem)); - return new HistorySamplerItem(data.collector, data.history, historyId, System.identityHashCode(data)); + List items = im.search("variableId", i.variableId); + Collections.sort(items, SamplingFormat.INTERVAL_COMPARATOR); + if (items.isEmpty()) + new DatabaseException("There is history item for " + i.variableId); + Bean config = items.get(0); + String historyId = (String) config.getFieldUnchecked("id"); - } catch (HistoryException e) { - throw new DatabaseException(e); - } catch (BindingException e) { - throw new DatabaseException(e); - } - - } + return new HistorySamplerItem(data.collector, data.history, historyId, System.identityHashCode(data)); + } catch (HistoryException e) { + throw new DatabaseException(e); + } catch (BindingException e) { + throw new DatabaseException(e); + } + } } diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/ITrendSupport.java b/bundles/org.simantics.charts/src/org/simantics/charts/ITrendSupport.java index c7a1f84a2..d1c374e60 100644 --- a/bundles/org.simantics.charts/src/org/simantics/charts/ITrendSupport.java +++ b/bundles/org.simantics.charts/src/org/simantics/charts/ITrendSupport.java @@ -1,5 +1,6 @@ package org.simantics.charts; +import org.simantics.charts.editor.ChartData; import org.simantics.db.ReadGraph; import org.simantics.db.exception.DatabaseException; @@ -7,4 +8,6 @@ public interface ITrendSupport { void setChartData(ReadGraph graph) throws DatabaseException; + ChartData getChartData(); + } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java index ed8df095a..96cd573cc 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java @@ -11,8 +11,6 @@ *******************************************************************************/ package org.simantics.diagram.elements; -import gnu.trove.list.array.TIntArrayList; - import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; @@ -42,6 +40,7 @@ import java.text.AttributedString; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; +import java.util.List; import org.simantics.datatypes.literal.RGB; import org.simantics.db.layer0.variable.RVI; @@ -62,15 +61,17 @@ import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; +import org.simantics.scenegraph.g2d.events.NodeEventHandler; import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.scenegraph.g2d.events.command.Commands; -import org.simantics.scenegraph.g2d.events.NodeEventHandler; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.scenegraph.utils.NodeUtil; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.Function2; import org.simantics.ui.colors.Colors; import org.simantics.ui.dnd.LocalObjectTransferable; +import org.simantics.ui.dnd.MultiTransferable; +import org.simantics.ui.dnd.PlaintextTransfer; import org.simantics.ui.fonts.Fonts; import org.simantics.utils.threads.AWTThread; @@ -82,6 +83,8 @@ import com.lowagie.text.pdf.PdfFormField; import com.lowagie.text.pdf.PdfWriter; import com.lowagie.text.pdf.TextField; +import gnu.trove.list.array.TIntArrayList; + /** * TextNode which supports in-line editing. @@ -1858,9 +1861,19 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L if (isHovering() && (isControlDown(e) || isShiftDown(e)) && e.context instanceof NodeEventHandler - && dataRVI != null) + && (dataRVI != null || text != null)) { - e.transferable = new LocalObjectTransferable(dataRVI); + List trs = new ArrayList<>(2); + if (dataRVI != null) { + trs.add(new LocalObjectTransferable(dataRVI)); + trs.add(new PlaintextTransfer(dataRVI.toString())); + } else if (text != null && !text.isEmpty()) { + trs.add(new PlaintextTransfer(text)); + } + if (!trs.isEmpty()) { + e.transferable = new MultiTransferable(trs); + return true; + } } return false; } @@ -1930,7 +1943,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @Override public int getEventMask() { return EventTypes.KeyPressedMask | EventTypes.MouseMovedMask | EventTypes.MouseButtonPressedMask - | EventTypes.MouseClickMask | EventTypes.CommandMask; + | EventTypes.MouseClickMask | EventTypes.MouseDragBeginMask | EventTypes.CommandMask; } private MouseEvent lastMouseEvent = null; diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/WorkbenchSelectionProvider.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/WorkbenchSelectionProvider.java index 096fd7aae..aa5076278 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/WorkbenchSelectionProvider.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/ui/WorkbenchSelectionProvider.java @@ -13,6 +13,7 @@ package org.simantics.diagram.ui; import java.util.ArrayList; import java.util.Collections; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; @@ -22,14 +23,18 @@ import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.ui.IWorkbenchPartSite; +import org.simantics.Logger; +import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.elements.AdaptableImmutableProxyElement; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.HintReflection.HintListener; +import org.simantics.g2d.diagram.participant.ElementJSON; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.IElement; +import org.simantics.ui.selection.WorkbenchSelectionUtils; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.threads.IThreadWorkQueue; @@ -41,7 +46,7 @@ import org.simantics.utils.threads.ThreadUtils; * * @author Tuukka Lehtonen */ -public class WorkbenchSelectionProvider extends AbstractCanvasParticipant implements IPostSelectionProvider { +public class WorkbenchSelectionProvider extends AbstractCanvasParticipant implements IPostSelectionProvider, ElementJSON { private static final long POST_SELECTION_DELAY = 300; @@ -205,4 +210,15 @@ public class WorkbenchSelectionProvider extends AbstractCanvasParticipant implem System.out.println("WorkbenchSelectionProvider: TODO: set selection: " + selection); } + @Override + public Optional getJSON(IElement element) { + ISelection sel = constructAdaptableSelection(Collections.singleton(element)); + try { + return Optional.ofNullable( WorkbenchSelectionUtils.getPossibleJSON(sel) ); + } catch (DatabaseException e) { + Logger.defaultLogError(e); + return Optional.empty(); + } + } + } diff --git a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/IAbstractRequestInterface.java b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/IAbstractRequestInterface.java index 041953490..c7bd29392 100644 --- a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/IAbstractRequestInterface.java +++ b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/IAbstractRequestInterface.java @@ -6,6 +6,7 @@ public interface IAbstractRequestInterface { void createSession(String sessionGUID, IConsole console); void deleteSession(String sessionGUID); void setInputURI(String sessionGUID, String inputURI); + void setSessionParameter(String sessionGUID, String parameterName, Object value); public IJSONResult requestDocument(String document, int sequenceNumber, int pollingHint, String sessionGUID); public String requestDocumentURI(String alias); String requestCSS(String documentURI); diff --git a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java index 794b9e55b..c9e438af6 100644 --- a/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java +++ b/bundles/org.simantics.document.server.io/src/org/simantics/document/server/io/JSONObjectUtils.java @@ -62,7 +62,11 @@ public class JSONObjectUtils { public static boolean selected(IJSONObject object) { return getValueOrDefault(object, "selected", false); } - + + public static boolean followEditMode(IJSONObject object) { + return getValueOrDefault(object, "followEditMode", true); + } + public static String getType(IJSONObject object) { String result = (String)object.getValue("type"); if(result == null) throw new IllegalStateException("No type for object " + object); diff --git a/bundles/org.simantics.document.server/scl/Document/All.scl b/bundles/org.simantics.document.server/scl/Document/All.scl index 3d63eaf6a..1f567a6fc 100644 --- a/bundles/org.simantics.document.server/scl/Document/All.scl +++ b/bundles/org.simantics.document.server/scl/Document/All.scl @@ -1,9 +1,11 @@ import "Simantics/DB" import "Simantics/Variables" +import "JavaBuiltin" as Java importJava "org.simantics.document.server.io.IConsole" where data IConsole addMessage :: IConsole -> String -> () + sendEvent :: IConsole -> String -> String -> Vector Dynamic -> () importJava "org.simantics.document.server.io.IDocument" where data IDocument @@ -63,11 +65,19 @@ importJava "org.simantics.document.server.io.CommandContextMutable" where putValue :: CommandContextMutable -> String -> a -> CommandContextMutable + merge :: CommandContextMutable -> CommandContext -> CommandContextMutable + importJava "org.simantics.document.server.io.CommandContextImpl" where @JavaName create commandContext :: () -> CommandContextMutable +instance Coercible CommandContextMutable CommandContext where + coerce = Java.unsafeCoerce + +cloneCommandContext :: CommandContext -> CommandContextMutable +cloneCommandContext context = merge (commandContext ()) context + importJava "org.simantics.document.server.io.CommandResult" where data CommandResult diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java index ab72524b3..ca82be6c9 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLHandlerValueRequest.java @@ -1,5 +1,6 @@ package org.simantics.document.server.request; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -8,16 +9,21 @@ import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.request.IndexRoot; -import org.simantics.db.common.request.UnaryRead; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.scl.AbstractExpressionCompilationContext; import org.simantics.db.layer0.scl.AbstractExpressionCompilationRequest; import org.simantics.db.layer0.util.RuntimeEnvironmentRequest; import org.simantics.db.layer0.variable.Variable; +import org.simantics.document.server.Functions; +import org.simantics.document.server.bean.DataDefinition; import org.simantics.document.server.request.ServerSCLHandlerValueRequest.CompilationContext; import org.simantics.layer0.Layer0; +import org.simantics.scl.compiler.constants.StringConstant; import org.simantics.scl.compiler.elaboration.expressions.EApply; import org.simantics.scl.compiler.elaboration.expressions.EConstant; +import org.simantics.scl.compiler.elaboration.expressions.ELiteral; +import org.simantics.scl.compiler.elaboration.expressions.EVar; import org.simantics.scl.compiler.elaboration.expressions.EVariable; import org.simantics.scl.compiler.elaboration.expressions.Expression; import org.simantics.scl.compiler.elaboration.modules.SCLValue; @@ -37,6 +43,7 @@ import org.simantics.utils.datastructures.Pair; public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationRequest { + private final Variable context; private final Pair componentTypeAndRoot; private final Resource literal; @@ -50,14 +57,15 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR } } - private ServerSCLHandlerValueRequest(Pair componentTypeAndRoot, Resource literal) { + private ServerSCLHandlerValueRequest(Variable context, Pair componentTypeAndRoot, Resource literal) { assert(literal != null); + this.context = context; this.literal = literal; this.componentTypeAndRoot = componentTypeAndRoot; } public ServerSCLHandlerValueRequest(ReadGraph graph, Variable context) throws DatabaseException { - this(getComponentTypeAndRoot(graph, context), context.getRepresents(graph)); + this(context, getComponentTypeAndRoot(graph, context), context.getRepresents(graph)); } private static Pair getComponentTypeAndRoot(ReadGraph graph, Variable property) throws DatabaseException { @@ -122,19 +130,31 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR } @Override - protected CompilationContext getCompilationContext(ReadGraph graph) - throws DatabaseException { - return graph.syncRequest(new UnaryRead,CompilationContext>(componentTypeAndRoot) { + protected CompilationContext getCompilationContext(ReadGraph graph) throws DatabaseException { + + return graph.syncRequest(new VariableRead(context) { + @Override - public CompilationContext perform(ReadGraph graph) - throws DatabaseException { + public CompilationContext perform(ReadGraph graph) throws DatabaseException { + + Pair parameter = getComponentTypeAndRoot(graph, variable); RuntimeEnvironment runtimeEnvironment = graph.syncRequest(getRuntimeEnvironmentRequest(parameter.second)); + Map propertyMap = graph.syncRequest(new ReadComponentTypeInterfaceRequest(parameter.first, runtimeEnvironment.getEnvironment()), TransientCacheListener.>instance()); + +// Map result = new HashMap(propertyMap); +// for(DataDefinition dd : Functions.dataDefinitions(graph, variable)) { +// result.put(dd.target, null); +// } + return new CompilationContext(runtimeEnvironment, propertyMap); + } + }); + } @Override @@ -158,7 +178,6 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR return getPropertyFlexible(environment, accessInputVariable(environment, contextVariable), name, type); } - @Override protected Expression getVariableAccessExpression( ReadGraph graph, @@ -172,8 +191,26 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR contextVariable, name, property.type == null ? Types.metaVar(Kinds.STAR) : property.type); - else + else { + +// if(context.propertyMap.containsKey(name)) { +// +// org.simantics.scl.compiler.elaboration.expressions.Variable parametersVariable = new org.simantics.scl.compiler.elaboration.expressions.Variable("context", COMMAND_CONTEXT); +// +// Environment environment = context.runtimeEnvironment.getEnvironment(); +// +//// return new EApply( +//// new EConstant(environment.getValue(FROM_DYNAMIC), Types.STRING), +// return new EApply( +// new EConstant(environment.getValue(CONTEXT_VARIABLE), Types.DYNAMIC), +// new EVariable(parametersVariable), +// new ELiteral(new StringConstant(name))); +// +// } + return getSpecialVariableAccessExpression(graph, context, contextVariable, name); + + } } protected Expression getSpecialVariableAccessExpression(ReadGraph graph, @@ -191,16 +228,42 @@ public class ServerSCLHandlerValueRequest extends AbstractExpressionCompilationR @Override public int hashCode() { - return 31*(31*getClass().hashCode() + literal.hashCode()) + componentTypeAndRoot.hashCode(); + final int prime = 31; + int result = 1; + result = prime * result + ((context == null) ? 0 : context.hashCode()); + return result; } @Override public boolean equals(Object obj) { - if(this == obj) + if (this == obj) return true; - if(obj == null || obj.getClass() != getClass()) + if (obj == null) + return false; + if (getClass() != obj.getClass()) return false; - ServerSCLHandlerValueRequest other = (ServerSCLHandlerValueRequest)obj; - return literal.equals(other.literal) && componentTypeAndRoot.equals(other.componentTypeAndRoot); + ServerSCLHandlerValueRequest other = (ServerSCLHandlerValueRequest) obj; + if (context == null) { + if (other.context != null) + return false; + } else if (!context.equals(other.context)) + return false; + return true; } + +// @Override +// public int hashCode() { +// return 31*(31*getClass().hashCode() + literal.hashCode()) + componentTypeAndRoot.hashCode(); +// } +// +// @Override +// public boolean equals(Object obj) { +// if(this == obj) +// return true; +// if(obj == null || obj.getClass() != getClass()) +// return false; +// ServerSCLHandlerValueRequest other = (ServerSCLHandlerValueRequest)obj; +// return literal.equals(other.literal) && componentTypeAndRoot.equals(other.componentTypeAndRoot); +// } + } diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementJSON.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementJSON.java new file mode 100644 index 000000000..ffeb83fcf --- /dev/null +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementJSON.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2016 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.g2d.diagram.participant; + +import java.util.Optional; + +import org.simantics.g2d.canvas.ICanvasParticipant; +import org.simantics.g2d.element.IElement; + +/** + * @author Antti Villberg + */ +public interface ElementJSON extends ICanvasParticipant { + + /** + * @param element + * @return JSON that describes the provided diagram element + */ + public Optional getJSON(IElement element); + +} diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java index 977f2be25..a35b65ccc 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementPainter.java @@ -557,6 +557,7 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos ConnectionNode holder = e.getHint(sgKey); if (holder == null) { holder = parentNode.addNode(ElementUtils.generateNodeId(e), ConnectionNode.class); + holder.setTransferableProvider(new ElementTransferableProvider(getContext(), e)); e.setHint(sgKey, holder); holder.setZIndex(parentNode.getNodeCount() + 1); } @@ -566,6 +567,7 @@ public class ElementPainter extends AbstractDiagramParticipant implements Compos SingleElementNode holder = e.getHint(sgKey); if (holder == null) { holder = parentNode.addNode(ElementUtils.generateNodeId(e), SingleElementNode.class); + holder.setTransferableProvider(new ElementTransferableProvider(getContext(), e)); e.setHint(sgKey, holder); holder.setZIndex(parentNode.getNodeCount() + 1); } diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementTransferableProvider.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementTransferableProvider.java new file mode 100644 index 000000000..6d0749692 --- /dev/null +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/ElementTransferableProvider.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2016 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.g2d.diagram.participant; + +import java.awt.datatransfer.Transferable; + +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.element.IElement; +import org.simantics.scenegraph.g2d.nodes.TransferableProvider; +import org.simantics.ui.dnd.PlaintextTransfer; + +/** + * @author Antti Villberg, Tuukka Lehtonen + */ +public class ElementTransferableProvider implements TransferableProvider { + + final private ICanvasContext context; + final private IElement element; + + public ElementTransferableProvider(ICanvasContext context, IElement element) { + this.context = context; + this.element = element; + } + + @Override + public Transferable create() { + ElementJSON converter = context.getAtMostOneItemOfClass(ElementJSON.class); + if (converter == null) + return null; + return converter.getJSON(element).map(PlaintextTransfer::new).orElse(null); + } + +} diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java index a062eb76b..8a0ee6ef1 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java @@ -11,6 +11,7 @@ import org.simantics.databoard.binding.Binding; import org.simantics.databoard.type.Datatype; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.NodeSupport; +import org.simantics.modeling.ModelingResources; import org.simantics.scl.compiler.types.Type; import org.simantics.simulator.variable.Realm; import org.simantics.simulator.variable.exceptions.NodeManagerException; @@ -272,6 +273,6 @@ public class SCLNodeManager extends AbstractNodeManager { @Override public String getPropertyURI(String parent, String property) { - return "http://www.simantics.org/Modeling-1.2/CommandSession/hasValue"; + return ModelingResources.URIs.SCLCommandSession_hasValue; } } diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/NodeEventHandler.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/NodeEventHandler.java index d1f6e6ef2..ac0e36f73 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/NodeEventHandler.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/NodeEventHandler.java @@ -20,10 +20,12 @@ import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; +import java.awt.event.InputEvent; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.G2DFocusManager; @@ -31,6 +33,7 @@ import org.simantics.scenegraph.g2d.G2DSceneGraph; import org.simantics.scenegraph.g2d.IG2DNode; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; +import org.simantics.scenegraph.g2d.events.adapter.AWTMouseEventAdapter; import org.simantics.scenegraph.g2d.events.command.CommandEvent; /** @@ -44,6 +47,8 @@ public class NodeEventHandler implements IEventHandler { private static final boolean DEBUG_EVENTS = false; private static final boolean DEBUG_HANDLER_SORT = false; + private static final IEventHandler[] NONE = {}; + public static class TreePreOrderComparator implements Comparator { static enum Order { @@ -146,120 +151,129 @@ public class NodeEventHandler implements IEventHandler { TreePreOrderComparator COMPARATOR = new TreePreOrderComparator(TreePreOrderComparator.Order.DESCENDING); /** - * FocusEvents are propagated to event handlers in undefined order. + * {@link FocusEvent} are propagated first to the scene graph focus node, + * then to event handler nodes in scene graph tree pre-order. */ - protected ListenerList focusListeners = new ListenerList(IEventHandler.class); + protected List focusListeners = new ArrayList(); + protected IEventHandler[] sortedFocusListeners = null; /** - * TimeEvents are propagated to events handlers in an undefined order. + * {@link TimeEvent} are propagated first to the scene graph focus node, + * then to event handler nodes in scene graph tree pre-order. */ - protected ListenerList timeListeners = new ListenerList(IEventHandler.class); + protected List timeListeners = new ArrayList(); + protected IEventHandler[] sortedTimeListeners = null; /** - * CommandEvents are propagated first to the scene graph focus node, then to - * event handler nodes in scene graph tree pre-order. + * {@link CommandEvent} are propagated first to the scene graph focus node, + * then to event handler nodes in scene graph tree pre-order. */ - protected ListenerList commandListeners = new ListenerList(IEventHandler.class); + protected List commandListeners = new ArrayList(); protected IEventHandler[] sortedCommandListeners = null; /** - * KeyEvents are propagated first to the scene graph focus node, then to - * event handler nodes in scene graph tree pre-order. + * {@link KeyEvent} are propagated first to the scene graph focus node, then + * to event handler nodes in scene graph tree pre-order. */ - protected ListenerList keyListeners = new ListenerList(IEventHandler.class); + protected List keyListeners = new ArrayList(); protected IEventHandler[] sortedKeyListeners = null; /** - * MouseEvents are propagated first to the scene graph focus node, then to - * event handler nodes in scene graph tree pre-order. + * {@link MouseEvent} are propagated first to the scene graph focus node, + * then to event handler nodes in scene graph tree pre-order. */ - protected ListenerList mouseListeners = new ListenerList(IEventHandler.class); + protected List mouseListeners = new ArrayList(); protected IEventHandler[] sortedMouseListeners = null; + + /** + * {@link MouseDragBegin} events are propagated first to the scene graph focus node, then + * to event handler nodes in scene graph tree pre-order. + */ + protected List mouseDragBeginListeners = new ArrayList(); + protected IEventHandler[] sortedMouseDragBeginListeners = null; /** * The scene graph this instance handles event propagation for. */ protected G2DSceneGraph sg; - - protected DragSource ds = new DragSource(); + + /** + * For proper initiation of native DnD operations within this AWT-based + * scenegraph system. + */ + protected DragSource ds = new DragSource(); public NodeEventHandler(G2DSceneGraph sg) { this.sg = sg; } + @SuppressWarnings("unused") private IEventHandler[] sort(IEventHandler[] sort) { if (DEBUG_HANDLER_SORT) - debug("sort " + sort.length + " handlers"); - IEventHandler[] copy = Arrays.copyOf(sort, sort.length); - Arrays.sort(copy, COMPARATOR); - return copy; + debug("copy sort " + sort.length + " handlers"); + return sortInplace(Arrays.copyOf(sort, sort.length)); } - - public void setRootPane(Component rootPane) { - - final DragSourceListener dsl = new DragSourceListener() { - - @Override - public void dropActionChanged(DragSourceDragEvent dsde) { - } - - @Override - public void dragOver(DragSourceDragEvent dsde) { - } - - @Override - public void dragExit(DragSourceEvent dse) { - } - - @Override - public void dragEnter(DragSourceDragEvent dsde) { - } - - @Override - public void dragDropEnd(DragSourceDropEvent dsde) { - } - }; - ds.createDefaultDragGestureRecognizer(rootPane, DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK, new DragGestureListener() { - - @Override - public void dragGestureRecognized(DragGestureEvent dge) { - MouseDragBegin event = new MouseDragBegin(NodeEventHandler.this, - 0, 0, 0, 0, 0, - new Point2D.Double(),new Point2D.Double(), - new Point2D.Double(),new Point2D.Double()); - handleMouseEvent(event, EventTypes.MouseDragBegin); - if(event.transferable != null) { - ds.startDrag(dge, null, event.transferable, dsl); - if (DEBUG_EVENTS) - debug("dragGestureRecognized: startDrag " + event.transferable); - } - } - }); - ds.addDragSourceListener(dsl); - + + private IEventHandler[] sortInplace(IEventHandler[] sort) { + if (DEBUG_HANDLER_SORT) + debug("in-place sort " + sort.length + " handlers"); + Arrays.sort(sort, COMPARATOR); + return sort; } -// @Override -// public void mouseReleased(MouseEvent event) { -// Point op = event.getPoint(); -// for (MouseListener l : mouseListeners.getListeners()) { -// MouseEvent e = (MouseEvent) NodeUtil.transformEvent(event,(IG2DNode) l); -// l.mouseReleased(e); -// event.translatePoint((int)(op.getX()-event.getX()), (int)(op.getY()-event.getY())); -// if (e.isConsumed()) -// break; -// } -// } -// -// @Override -// public void mouseMoved(MouseEvent event) { -// for (MouseMotionListener l : mouseMotionListeners.getListeners()) { -// MouseEvent e = (MouseEvent) NodeUtil.transformEvent(event,(IG2DNode) l); -// l.mouseMoved(e); -// if (e.isConsumed()) -// break; -// } -// } + public void setRootPane(Component rootPane) { + final DragSourceListener dsl = new DragSourceListener() { + @Override + public void dropActionChanged(DragSourceDragEvent dsde) { + } + @Override + public void dragOver(DragSourceDragEvent dsde) { + } + @Override + public void dragExit(DragSourceEvent dse) { + } + @Override + public void dragEnter(DragSourceDragEvent dsde) { + } + @Override + public void dragDropEnd(DragSourceDropEvent dsde) { + } + }; + DragGestureListener dgl = new DragGestureListener() { + @Override + public void dragGestureRecognized(DragGestureEvent dge) { + InputEvent ie = dge.getTriggerEvent(); + if (ie instanceof java.awt.event.MouseEvent) { + java.awt.event.MouseEvent e = (java.awt.event.MouseEvent) ie; + Point2D controlPos = AWTMouseEventAdapter.getControlPosition(e); + MouseDragBegin event = new MouseDragBegin(NodeEventHandler.this, + e.getWhen(), 0, + AWTMouseEventAdapter.getButtonStatus(e), + AWTMouseEventAdapter.getStateMask(e), + AWTMouseEventAdapter.getMouseButton(e), + // TODO: fix canvas position if necessary + new Point2D.Double(), + controlPos, + controlPos, + AWTMouseEventAdapter.getScreenPosition(e)); + + // Send MouseDragBegin to the scenegraph and see + // if anyone sets event.transferable to start DnD. + handleMouseDragBeginEvent(event, EventTypes.MouseDragBegin); + if (event.transferable != null) { + ds.startDrag(dge, null, event.transferable, dsl); + if (DEBUG_EVENTS) + debug("dragGestureRecognized: startDrag " + event.transferable); + } + } + } + }; + ds.createDefaultDragGestureRecognizer( + rootPane, + DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK, + dgl); + ds.addDragSourceListener(dsl); + } public boolean mousePressed(MouseButtonPressedEvent event) { G2DFocusManager.INSTANCE.clearFocus(); @@ -283,47 +297,65 @@ public class NodeEventHandler implements IEventHandler { } } - private boolean handleMouseEvent(MouseEvent e, int eventType) { - IEventHandler[] sorted = sortedMouseListeners; - if (sorted == null) - sortedMouseListeners = sorted = sort(mouseListeners.getListeners()); - return handleEvent(e, sg.getFocusNode(), sorted); - } - private boolean handleEvent(Event e, IG2DNode focusNode, IEventHandler[] handlers) { + int typeMask = EventTypes.toTypeMask(e); if (focusNode instanceof IEventHandler) { IEventHandler h = (IEventHandler) focusNode; - if (eats(h.getEventMask(), EventTypes.toTypeMask(e))) { + if (eats(h.getEventMask(), typeMask)) { if (h.handleEvent(e)) return true; } } for (IEventHandler l : handlers) { - if (l.handleEvent(e)) - return true; + if (eats(l.getEventMask(), typeMask)) { + if (l.handleEvent(e)) + return true; + } } return false; } + private boolean handleMouseEvent(MouseEvent e, int eventType) { + IEventHandler[] sorted = sortedMouseListeners; + if (sorted == null) + sortedMouseListeners = sorted = sortInplace(mouseListeners.toArray(NONE)); + return handleEvent(e, sg.getFocusNode(), sorted); + } + + private boolean handleMouseDragBeginEvent(MouseEvent e, int eventType) { + IEventHandler[] sorted = sortedMouseDragBeginListeners; + if (sorted == null) + sortedMouseDragBeginListeners = sorted = sortInplace(mouseDragBeginListeners.toArray(NONE)); + // Give null for focusNode because we want to propagate + // this event in scene tree pre-order only. + return handleEvent(e, null, sorted); + } + private boolean handleFocusEvent(FocusEvent e) { - return handleEvent(e, null, focusListeners.getListeners()); + IEventHandler[] sorted = sortedFocusListeners; + if (sorted == null) + sortedFocusListeners = sorted = sortInplace(focusListeners.toArray(NONE)); + return handleEvent(e, null, sorted); } private boolean handleTimeEvent(TimeEvent e) { - return handleEvent(e, null, timeListeners.getListeners()); + IEventHandler[] sorted = sortedTimeListeners; + if (sorted == null) + sortedTimeListeners = sorted = sortInplace(timeListeners.toArray(NONE)); + return handleEvent(e, null, sorted); } private boolean handleCommandEvent(CommandEvent e) { IEventHandler[] sorted = sortedCommandListeners; if (sorted == null) - sortedCommandListeners = sorted = sort(commandListeners.getListeners()); + sortedCommandListeners = sorted = sortInplace(commandListeners.toArray(NONE)); return handleEvent(e, sg.getFocusNode(), sorted); } private boolean handleKeyEvent(KeyEvent e) { IEventHandler[] sorted = sortedKeyListeners; if (sorted == null) - sortedKeyListeners = sorted = sort(keyListeners.getListeners()); + sortedKeyListeners = sorted = sortInplace(keyListeners.toArray(NONE)); return handleEvent(e, sg.getFocusNode(), sorted); } @@ -350,11 +382,13 @@ public class NodeEventHandler implements IEventHandler { case EventTypes.KeyReleased: return handleKeyEvent((KeyEvent) e); + case EventTypes.MouseDragBegin: + return handleMouseDragBeginEvent((MouseEvent) e, eventType); + case EventTypes.MouseButtonPressed: case EventTypes.MouseButtonReleased: case EventTypes.MouseClick: case EventTypes.MouseDoubleClick: - case EventTypes.MouseDragBegin: case EventTypes.MouseEnter: case EventTypes.MouseExit: case EventTypes.MouseMoved: @@ -378,17 +412,23 @@ public class NodeEventHandler implements IEventHandler { } if (eats(mask, EventTypes.FocusMask)) { focusListeners.add(item); + sortedFocusListeners = null; } if (eats(mask, EventTypes.KeyMask)) { keyListeners.add(item); sortedKeyListeners = null; } - if (eats(mask, EventTypes.MouseMask)) { + if (eats(mask, EventTypes.MouseDragBeginMask)) { + mouseDragBeginListeners.add(item); + sortedMouseDragBeginListeners = null; + } + if (eats(mask, EventTypes.MouseMask & ~EventTypes.MouseDragBeginMask)) { mouseListeners.add(item); sortedMouseListeners = null; } if (eats(mask, EventTypes.TimeMask)) { timeListeners.add(item); + sortedTimeListeners = null; } } @@ -404,17 +444,23 @@ public class NodeEventHandler implements IEventHandler { } if (eats(mask, EventTypes.FocusMask)) { removed |= focusListeners.remove(item); + sortedFocusListeners = null; } if (eats(mask, EventTypes.KeyMask)) { removed |= keyListeners.remove(item); sortedKeyListeners = null; } - if (eats(mask, EventTypes.MouseMask)) { + if (eats(mask, EventTypes.MouseDragBeginMask)) { + removed |= mouseDragBeginListeners.remove(item); + sortedMouseDragBeginListeners = null; + } + if (eats(mask, EventTypes.MouseMask & ~EventTypes.MouseDragBeginMask)) { removed |= mouseListeners.remove(item); sortedMouseListeners = null; } if (eats(mask, EventTypes.TimeMask)) { removed |= timeListeners.remove(item); + sortedTimeListeners = null; } return removed; } diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/adapter/AWTMouseEventAdapter.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/adapter/AWTMouseEventAdapter.java index 97d0e01f9..85f56a34f 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/adapter/AWTMouseEventAdapter.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/events/adapter/AWTMouseEventAdapter.java @@ -71,17 +71,17 @@ public class AWTMouseEventAdapter extends AbstractEventAdapter implements MouseL super(sender, queue); } - private Point2D getControlPosition(MouseEvent e) + public static Point2D getControlPosition(MouseEvent e) { return new Point2D.Double(e.getX(), e.getY()); } - private Point2D getScreenPosition(MouseEvent e) + public static Point2D getScreenPosition(MouseEvent e) { return e.getLocationOnScreen(); } - private static int getStateMask(MouseEvent e) { + public static int getStateMask(MouseEvent e) { int modifiers = e.getModifiersEx(); int stateMask = 0; if((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) @@ -95,7 +95,7 @@ public class AWTMouseEventAdapter extends AbstractEventAdapter implements MouseL return stateMask; } - private int getMouseButton(MouseEvent e) + public static int getMouseButton(MouseEvent e) { int awtMouseButton = e.getButton(); if (awtMouseButton == MouseEvent.BUTTON1) @@ -115,7 +115,7 @@ public class AWTMouseEventAdapter extends AbstractEventAdapter implements MouseL * @param e * @return */ - private int getButtonStatus(MouseEvent e) { + public static int getButtonStatus(MouseEvent e) { int modex = e.getModifiersEx(); int status = 0; status |= (modex & MouseEvent.BUTTON1_DOWN_MASK) != 0 ? org.simantics.scenegraph.g2d.events.MouseEvent.LEFT_MASK : 0; diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/ConnectionNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/ConnectionNode.java index 128b0df90..927619e02 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/ConnectionNode.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/ConnectionNode.java @@ -14,9 +14,12 @@ package org.simantics.scenegraph.g2d.nodes; import java.awt.Color; import java.awt.Composite; import java.awt.Stroke; +import java.awt.geom.Point2D; +import org.simantics.diagram.connection.RouteGraph; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode; import org.simantics.scenegraph.utils.InitValueSupport; import org.simantics.scenegraph.utils.NodeUtil; @@ -126,4 +129,25 @@ public class ConnectionNode extends SingleElementNode implements InitValueSuppor } } + @Override + protected boolean hitTest(MouseEvent event) { + if (!super.hitTest(event)) + return false; + + Point2D pos = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double()); + double tolerance = 0.5; + // TODO: change tolerance based on zoom level + + for (IG2DNode child : getSortedNodes()) { + if (child instanceof RouteGraphNode) { + RouteGraphNode rgn = (RouteGraphNode) child; + RouteGraph rg = rgn.getRouteGraph(); + Object pick = rg.pickLine(pos.getX(), pos.getY(), tolerance); + if (pick != null) + return true; + } + } + return false; + } + } diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java index dc40036a7..c6a8b950e 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java @@ -14,20 +14,35 @@ package org.simantics.scenegraph.g2d.nodes; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.events.EventTypes; +import org.simantics.scenegraph.g2d.events.MouseEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; import org.simantics.scenegraph.utils.InitValueSupport; +import org.simantics.scenegraph.utils.NodeUtil; public class SingleElementNode extends TransformNode implements InitValueSupport { - /** - * - */ + private static final long serialVersionUID = -4982578347433716440L; + private TransferableProvider transferableProvider; protected Composite composite; protected Boolean visible = Boolean.TRUE; protected Boolean hidden = Boolean.FALSE; + public void setTransferableProvider(TransferableProvider transferableProvider) { + if (transferableProvider != this.transferableProvider) { + if (this.transferableProvider != null) + removeEventHandler(this); + if (transferableProvider != null) + addEventHandler(this); + this.transferableProvider = transferableProvider; + } + } + @Override public boolean validate() { return visible && !hidden; @@ -97,34 +112,6 @@ public class SingleElementNode extends TransformNode implements InitValueSupport return sb.toString(); } -// private INode findChild(String key) { -// -// INode result = getNode(key); -// if(result != null) return result; -// -// for(Map.Entry entry : children.entrySet()) { -// -// if(!(entry.getValue() instanceof SingleElementNode)) { -// if(entry.getKey().startsWith(key)) return entry.getValue(); -// } -// -// } -// -// for(IG2DNode node : getNodes()) { -// -// if(node instanceof SingleElementNode) { -// -// result = ((SingleElementNode)node).findChild(key); -// if(result != null) return result; -// -// } -// -// } -// -// return null; -// -// } - protected Composite alphaComposite = null; @PropertySetter("alpha") @@ -133,26 +120,6 @@ public class SingleElementNode extends TransformNode implements InitValueSupport this.alphaComposite = alphaComposite; } -// public void setValue(String key, Object value) { -// -// if ("alpha".equals(key)) { -// Float val = Float.parseFloat((String)value); -// alphaComposite = AlphaComposite.getInstance(AlphaComposite. SRC_OVER, val); -// } else { -// -// String[] parts = key.split("\\."); -// if(parts.length != 2) return; -// -// INode child = findChild(parts[0]); -// -// if(child == null) return; -// -//// child.setValue(parts[1], value); -// -// } -// -// } - @Override public void initValues() { alphaComposite = null; @@ -163,4 +130,43 @@ public class SingleElementNode extends TransformNode implements InitValueSupport } } + @Override + public void cleanup() { + if (transferableProvider != null) { + removeEventHandler(this); + transferableProvider = null; + } + super.cleanup(); + } + + protected boolean isDragStartEvent(MouseEvent e) { + return e.isControlDown(); + } + + protected boolean hitTest(MouseEvent event) { + Rectangle2D bounds = super.getBoundsInLocal(false); + if (bounds == null) + return false; + Point2D localPos = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double()); + double x = localPos.getX(); + double y = localPos.getY(); + boolean hit = bounds.contains(x, y); + return hit; + } + + @Override + protected boolean mouseDragged(MouseDragBegin e) { + if (transferableProvider == null + || !isDragStartEvent(e) + || !hitTest(e)) + return false; + e.transferable = transferableProvider.create(); + return e.transferable != null; + } + + @Override + public int getEventMask() { + return EventTypes.MouseDragBeginMask; + } + } diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/TransferableProvider.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/TransferableProvider.java new file mode 100644 index 000000000..36ef357c4 --- /dev/null +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/TransferableProvider.java @@ -0,0 +1,9 @@ +package org.simantics.scenegraph.g2d.nodes; + +import java.awt.datatransfer.Transferable; + +public interface TransferableProvider { + + public Transferable create(); + +} diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/dnd/MultiTransferable.java b/bundles/org.simantics.ui/src/org/simantics/ui/dnd/MultiTransferable.java index 914f84a1a..54ec348cb 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/dnd/MultiTransferable.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/dnd/MultiTransferable.java @@ -6,6 +6,7 @@ import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -21,6 +22,14 @@ public class MultiTransferable implements Transferable, ClipboardOwner { } } + public MultiTransferable(Collection tr) { + for(Transferable t : tr) { + for(DataFlavor f : t.getTransferDataFlavors()) { + transferables.put(f, t); + } + } + } + @Override public void lostOwnership(Clipboard arg0, Transferable arg1) { for(Transferable t : transferables.values()) @@ -44,4 +53,9 @@ public class MultiTransferable implements Transferable, ClipboardOwner { return transferables.containsKey(arg0); } + @Override + public String toString() { + return "MultiTransferable " + transferables.toString(); + } + } diff --git a/bundles/org.simantics.ui/src/org/simantics/ui/dnd/PlaintextTransfer.java b/bundles/org.simantics.ui/src/org/simantics/ui/dnd/PlaintextTransfer.java index b54413a93..855051ed1 100644 --- a/bundles/org.simantics.ui/src/org/simantics/ui/dnd/PlaintextTransfer.java +++ b/bundles/org.simantics.ui/src/org/simantics/ui/dnd/PlaintextTransfer.java @@ -3,32 +3,38 @@ package org.simantics.ui.dnd; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.ByteArrayInputStream; import java.io.IOException; public class PlaintextTransfer implements Transferable { final String content; + final DataFlavor flavor = DataFlavor.getTextPlainUnicodeFlavor(); + final DataFlavor[] flavors = new DataFlavor[] { flavor }; + final String flavorCharset = flavor.getParameter("charset"); + public PlaintextTransfer(String content) { this.content = content; } @Override public Object getTransferData(DataFlavor arg0) throws UnsupportedFlavorException, IOException { - if(DataFlavor.getTextPlainUnicodeFlavor().equals(arg0)) { - return content; + if(flavor.equals(arg0)) { + byte[] bytes = content.getBytes(flavorCharset); + return new ByteArrayInputStream(bytes); } return null; } @Override public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { DataFlavor.getTextPlainUnicodeFlavor() }; + return flavors; } @Override public boolean isDataFlavorSupported(DataFlavor arg0) { - return(DataFlavor.getTextPlainUnicodeFlavor().equals(arg0)); + return flavor.equals(arg0); } } diff --git a/bundles/org.simantics.utils.ui.workbench/src/org/simantics/utils/ui/workbench/dialogs/ColorDialog.java b/bundles/org.simantics.utils.ui.workbench/src/org/simantics/utils/ui/workbench/dialogs/ColorDialog.java new file mode 100644 index 000000000..ab61e6dc7 --- /dev/null +++ b/bundles/org.simantics.utils.ui.workbench/src/org/simantics/utils/ui/workbench/dialogs/ColorDialog.java @@ -0,0 +1,137 @@ +package org.simantics.utils.ui.workbench.dialogs; + +import java.io.IOException; +import java.util.Deque; +import java.util.LinkedList; + +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.preference.IPersistentPreferenceStore; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.preferences.ScopedPreferenceStore; +import org.simantics.utils.ui.ExceptionUtils; +import org.simantics.utils.ui.color.Color; +import org.simantics.utils.ui.workbench.StringMemento; + +/** + * ColorDialog that keeps track used colors. + * + * @author Marko Luukkainen + * + */ +public class ColorDialog extends org.simantics.utils.ui.color.ColorDialog{ + + public ColorDialog(Shell parentShell) { + super(parentShell); + } + + + protected Control createDialogArea(Composite parent) { + initRecent(); + Composite composite = (Composite)super.createDialogArea(parent); + Group recentGroup = new Group(composite, SWT.NONE); + recentGroup.setText("Recent colors"); + GridLayoutFactory.fillDefaults().numColumns(8).spacing(2, 2).margins(4, 4).applyTo(recentGroup); + int i = 0; + for (Color c : recentColors) { + i++; + createColorButton(recentGroup, c); + } + org.eclipse.swt.graphics.Color c = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + Color color = new Color(c.getRGB()); + for ( ; i < MAX_RECENT_COLORS; i++) { + Button button = createColorButton(recentGroup, color); + button.setEnabled(false); + } + return composite; + } + + @Override + protected Point getInitialSize() { + return new Point(400, 450); + } + + private static final int MAX_RECENT_COLORS = 8; + + public static final String RECENT_COLORS = "RECENT_COLORS"; + + private static final String TAG_COLORS = "color"; + private static final String R_ATTR_NAME = "r"; + private static final String G_ATTR_NAME = "g"; + private static final String B_ATTR_NAME = "b"; + + private static final String PLUGIN_ID = "org.simantics.utils.ui.workbench"; + + protected Deque recentColors; + + private void initRecent() { + IPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, PLUGIN_ID); + String recentString = store.getString(RECENT_COLORS); + recentColors = decodeColors(recentString); + } + + private void storeRecent() { + Color color = getColor(); + recentColors.remove(color); + recentColors.addFirst(color); + while(recentColors.size() > MAX_RECENT_COLORS) + recentColors.removeLast(); + + IPersistentPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, PLUGIN_ID); + store.putValue(RECENT_COLORS, encodeColors(recentColors)); + + if (store.needsSaving()) + try { + store.save(); + } catch (IOException e) { + ExceptionUtils.logError(e); + } + } + + @Override + protected void okPressed() { + storeRecent(); + super.okPressed(); + } + + public static Deque decodeColors(String recentColorsPref) { + Deque result = new LinkedList(); + try { + StringMemento sm = new StringMemento(recentColorsPref); + for (IMemento m : sm.getChildren(TAG_COLORS)) { + try { + int r = m.getInteger(R_ATTR_NAME); + int g = m.getInteger(G_ATTR_NAME); + int b = m.getInteger(B_ATTR_NAME); + Color c = new Color(r, g, b); + result.add(c); + } catch (Exception e) { + + } + + } + } catch (IllegalArgumentException e) { + } + return result; + } + + public static String encodeColors(Deque recentColors) { + StringMemento sm = new StringMemento(); + for (Color c : recentColors) { + IMemento m = sm.createChild(TAG_COLORS); + m.putInteger(R_ATTR_NAME, c.getR()); + m.putInteger(G_ATTR_NAME, c.getG()); + m.putInteger(B_ATTR_NAME, c.getB()); + } + return sm.toString(); + } +} diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/Color.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/Color.java index b1f672890..da0251da1 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/Color.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/Color.java @@ -52,14 +52,14 @@ public class Color implements Comparable{ this.h = h; this.s = s; this.v = v; - updateRGB(); + updateRGB(); } public Color(double h, double s, double v) { this.h = (float)h; this.s = (float)s; this.v = (float)v; - updateRGB(); + updateRGB(); } public Color(RGB rgb) { @@ -223,7 +223,8 @@ public class Color implements Comparable{ } if (s == 0.f) { - h = Float.NaN; // saturation is 0 -> achromatic color + //h = Float.NaN; // saturation is 0 -> achromatic color + h = 0.f; } else { if (tr == v) { h = 60.f * (tg - tb); @@ -248,14 +249,9 @@ public class Color implements Comparable{ */ private void updateRGB() { if (s == 0.f) { - //if (Float.isNaN(h)) { - h = Float.NaN; - r = floatToInt(v); - g = floatToInt(v); - b = floatToInt(v); - //} else { - // throw new RuntimeException("Saturation is 0 -> Hue must be undefined"); - //} + r = floatToInt(v); + g = floatToInt(v); + b = floatToInt(v); } else { while (h < 0.f) h+= 360.f; diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorComposite.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorComposite.java index bae10b935..7e86056af 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorComposite.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorComposite.java @@ -11,15 +11,24 @@ *******************************************************************************/ package org.simantics.utils.ui.color; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Text; /** @@ -32,160 +41,525 @@ import org.eclipse.swt.widgets.Text; public class ColorComposite extends Composite{ - private Color color = new Color(255,255,255); + private Color color; private ColorGradient rGradient; private ColorGradient gGradient; private ColorGradient bGradient; + private ColorGradient hGradient; + private ColorGradient sGradient; + private ColorGradient vGradient; private ColorGradient colorGradient; + TabFolder tabFolder; + Text rText; Text gText; Text bText; - ColorGradientCanvas rCanvas; - ColorGradientCanvas gCanvas; - ColorGradientCanvas bCanvas; + IntGradientWidget rCanvas; + IntGradientWidget gCanvas; + IntGradientWidget bCanvas; ColorGradientCanvas colorCanvas; + Text hText; + Text sText; + Text vText; + GradientWidget hCanvas; + GradientWidget sCanvas; + GradientWidget vCanvas; + + Button dynamicButton; + + boolean dynamic = true; + public ColorComposite(Composite parent, int style) { super(parent,style); - updateGradients(); GridLayout layout = new GridLayout(); - layout.makeColumnsEqualWidth = false; - layout.numColumns = 3; + layout.makeColumnsEqualWidth = true; + layout.numColumns = 1; this.setLayout(layout); colorCanvas = new ColorGradientCanvas(this,SWT.BORDER|SWT.HORIZONTAL); - GridData colorCanvasData = new GridData(); - colorCanvasData.horizontalSpan = 3; - colorCanvasData.heightHint = 32; - colorCanvasData.widthHint = 600; - colorCanvasData.grabExcessHorizontalSpace = true; - colorCanvas.setLayoutData(colorCanvasData); - - //layout data used with labels - GridData labelData = new GridData(); - labelData.widthHint = 30; - //layout data used with gradients - GridData gradientData = new GridData(); - gradientData.grabExcessHorizontalSpace = true; - gradientData.heightHint = 16; - gradientData.widthHint = 500; - //layout data used with texts - GridData textData = new GridData(); - textData.widthHint = 30; - - - //Composite rComposite = new Composite(this,SWT.NONE); - //Layout rLayout = new FillLayout(SWT.HORIZONTAL); - //rComposite.setLayout(rLayout); - Label rLabel = new Label(this,SWT.NONE); + + tabFolder = new TabFolder(this, SWT.NONE); + + TabItem rgbTab = new TabItem(tabFolder, SWT.NONE); + rgbTab.setText("RGB"); + Composite rgbComposite = new Composite(tabFolder, SWT.NONE); + + rgbTab.setControl(rgbComposite); + + Label rLabel = new Label(rgbComposite,SWT.NONE); rLabel.setText("Red"); - rLabel.setLayoutData(labelData); - rCanvas = new ColorGradientCanvas(this,SWT.BORDER|SWT.HORIZONTAL); - rCanvas.setLayoutData(gradientData); - rText = new Text(this,SWT.BORDER); - rText.setLayoutData(textData); - rCanvas.addMouseListener(new ColorSelectListener(rCanvas) { + rCanvas = new IntGradientWidget(rgbComposite,SWT.BORDER|SWT.HORIZONTAL){ + @Override + protected void updatePosition(int value) { + Color newColor = new Color(value,color.getG(),color.getB()); + setColor(newColor); + } + }; + rText = new Text(rgbComposite,SWT.BORDER); + rText.addModifyListener(new IntColorModifyListener() { + + @Override void setValue(int value) { Color newColor = new Color(value,color.getG(),color.getB()); - updateColor(newColor); - }; + setColor(newColor); + } }); - //Composite gComposite = new Composite(this,SWT.NONE); - //Layout gLayout = new FillLayout(SWT.HORIZONTAL); - //gComposite.setLayout(gLayout); - Label gLabel = new Label(this,SWT.NONE); + Label gLabel = new Label(rgbComposite,SWT.NONE); gLabel.setText("Green"); - gLabel.setLayoutData(labelData); - gCanvas = new ColorGradientCanvas(this,SWT.BORDER|SWT.HORIZONTAL); - gCanvas.setLayoutData(gradientData); - gText = new Text(this,SWT.BORDER); - gText.setLayoutData(textData); - gCanvas.addMouseListener(new ColorSelectListener(gCanvas) { + gCanvas = new IntGradientWidget(rgbComposite,SWT.BORDER|SWT.HORIZONTAL) { + @Override + protected void updatePosition(int value) { + Color newColor = new Color(color.getR(),value,color.getB()); + setColor(newColor); + } + }; + gText = new Text(rgbComposite,SWT.BORDER); + gText.addModifyListener(new IntColorModifyListener() { + + @Override void setValue(int value) { Color newColor = new Color(color.getR(),value,color.getB()); - updateColor(newColor); - }; + setColor(newColor); + } }); - //Composite bComposite = new Composite(this,SWT.NONE); - //Layout bLayout = new FillLayout(SWT.HORIZONTAL); - //bComposite.setLayout(bLayout); - Label bLabel = new Label(this,SWT.NONE); + Label bLabel = new Label(rgbComposite,SWT.NONE); bLabel.setText("Blue"); - bLabel.setLayoutData(labelData); - bCanvas = new ColorGradientCanvas(this,SWT.BORDER|SWT.HORIZONTAL); - bCanvas.setLayoutData(gradientData); - bText = new Text(this,SWT.BORDER); - bText.setLayoutData(textData); - bCanvas.addMouseListener(new ColorSelectListener(bCanvas) { + bCanvas = new IntGradientWidget(rgbComposite,SWT.BORDER|SWT.HORIZONTAL) { + @Override + protected void updatePosition(int value) { + Color newColor = new Color(color.getR(),color.getG(),value); + setColor(newColor); + } + }; + bText = new Text(rgbComposite,SWT.BORDER); + bText.addModifyListener(new IntColorModifyListener() { + + @Override void setValue(int value) { Color newColor = new Color(color.getR(),color.getG(),value); - updateColor(newColor); - }; + setColor(newColor); + } }); - updateWidgets(); - } - - private abstract class ColorSelectListener implements MouseListener { - private Canvas canvas; + TabItem hsvTab = new TabItem(tabFolder, SWT.NONE); + hsvTab.setText("HSV"); + Composite hsvComposite = new Composite(tabFolder, SWT.NONE); - public ColorSelectListener(Canvas canvas) { - this.canvas = canvas; - } - public void mouseDoubleClick(MouseEvent e) { - - } - public void mouseDown(MouseEvent e) { - Rectangle r = this.canvas.getBounds(); - double d = (double)e.x / (double)r.width; - int value = (int)(d * 255); - if (value < 0) - value = 0; - if (value > 255) - value = 255; - setValue(value); + hsvTab.setControl(hsvComposite); + + Label hLabel = new Label(hsvComposite,SWT.NONE); + hLabel.setText("Hue"); + hCanvas = new GradientWidget(hsvComposite,SWT.BORDER|SWT.HORIZONTAL) { + @Override + protected boolean updatePosition(double d) { + Color newColor = new Color(d*360.0,color.getS(),color.getV()); + setColor(newColor); + return true; + } - } - public void mouseUp(MouseEvent e) { + @Override + protected void setPosition(double d) { + super.setPosition(d/360.0); + } + }; + hText = new Text(hsvComposite,SWT.BORDER); + hText.addModifyListener(new DoubleColorModifyListener(0.,360.) { + + @Override + void setValue(double value) { + Color newColor = new Color(value,color.getS(),color.getV()); + setColor(newColor); + } + }); + + Label sLabel = new Label(hsvComposite,SWT.NONE); + sLabel.setText("Saturation"); + sCanvas = new GradientWidget(hsvComposite,SWT.BORDER|SWT.HORIZONTAL) { + @Override + protected boolean updatePosition(double d) { + Color newColor = new Color(color.getH(),d,color.getV()); + setColor(newColor); + return true; + } + }; + sText = new Text(hsvComposite,SWT.BORDER); + sText.addModifyListener(new DoubleColorModifyListener(0.,1.) { + + @Override + void setValue(double value) { + Color newColor = new Color(color.getH(),value,color.getV()); + setColor(newColor); + } + }); + + Label vLabel = new Label(hsvComposite,SWT.NONE); + vLabel.setText("Value"); + vCanvas = new GradientWidget(hsvComposite,SWT.BORDER|SWT.HORIZONTAL) { + @Override + protected boolean updatePosition(double d) { + Color newColor = new Color(color.getH(),color.getS(),d); + setColor(newColor); + return true; + } + }; + vText = new Text(hsvComposite,SWT.BORDER); + vText.addModifyListener(new DoubleColorModifyListener(0.,1.) { + + @Override + void setValue(double value) { + Color newColor = new Color(color.getH(),color.getS(),value); + setColor(newColor); + } + }); + + TabItem settingsTab = new TabItem(tabFolder, SWT.NONE); + settingsTab.setText("Settings"); + Composite settingsComposite = new Composite(tabFolder, SWT.NONE); + + settingsTab.setControl(settingsComposite); + + dynamicButton = new Button(settingsComposite, SWT.CHECK); + dynamicButton.setText("Dynamic widgets"); + dynamicButton.setSelection(dynamic); + dynamicButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + dynamic = dynamicButton.getSelection(); + updateWidgets(); + } + }); + + GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(rgbComposite); + GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(hsvComposite); + GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(settingsComposite); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(rgbComposite); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(hsvComposite); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(settingsComposite); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(tabFolder); + + GridDataFactory.fillDefaults().hint(-1, 32).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(colorCanvas); + GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(rCanvas); + GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(gCanvas); + GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(bCanvas); + GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(hCanvas); + GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(sCanvas); + GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(vCanvas); + GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(rText); + GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(gText); + GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(bText); + GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(hText); + GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(sText); + GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(vText); + + if (color == null) + setColor(new Color(255,255,255)); + else + setColor(color); + } + + private abstract class IntColorModifyListener implements ModifyListener { + boolean modify = false; + @Override + public void modifyText(ModifyEvent e) { + if (internalUpdate) + return; + if (modify) + return; + modify = true; + Text text = (Text)e.widget; + try { + int value = Integer.parseInt(text.getText()); + if (value < 0) + value = 0; + if (value > 255) + value = 255; + setValue(value); + } catch (NumberFormatException err) { + + } + modify = false; } abstract void setValue(int value); } + + private abstract class DoubleColorModifyListener implements ModifyListener { + boolean modify = false; + double min; + double max; + + public DoubleColorModifyListener( double min, double max) { + this.min = min; + this.max = max; + } + + @Override + public void modifyText(ModifyEvent e) { + if (internalUpdate) + return; + if (modify) + return; + modify = true; + Text text = (Text)e.widget; + try { + double value = Integer.parseInt(text.getText()); + if (value < min) { + value = min; + } else if (value > max) + value = max; + setValue(value); + } catch (NumberFormatException err) { + + } + modify = false; + } + + + abstract void setValue(double value); + } - private void updateColor(Color color) { + public void setColor(Color color) { this.color = color; updateWidgets(); } + public Color getColor() { + return color; + } + private void updateGradients() { - rGradient = new ColorGradient(new ColorValue[]{ - new ColorValue(new Color(0,color.getG(),color.getB()),0.0), - new ColorValue(new Color(255,color.getG(),color.getB()),1.0)}); - gGradient = new ColorGradient(new ColorValue[]{ - new ColorValue(new Color(color.getR(),0,color.getB()),0.0), - new ColorValue(new Color(color.getR(),255,color.getB()),1.0)}); - bGradient = new ColorGradient(new ColorValue[]{ - new ColorValue(new Color(color.getR(),color.getG(),0),0.0), - new ColorValue(new Color(color.getR(),color.getG(),255),1.0)}); + if (dynamic) { + rGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(0,color.getG(),color.getB()),0.0), + new ColorValue(new Color(255,color.getG(),color.getB()),1.0)}); + gGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(color.getR(),0,color.getB()),0.0), + new ColorValue(new Color(color.getR(),255,color.getB()),1.0)}); + bGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(color.getR(),color.getG(),0),0.0), + new ColorValue(new Color(color.getR(),color.getG(),255),1.0)}); + // hue is interpolated along the shortest route, using just 0 and 360 would result in constant color. + hGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(0.0,color.getS(),color.getV()),0.0), + new ColorValue(new Color(90.0,color.getS(),color.getV()),0.25), + new ColorValue(new Color(180.0,color.getS(),color.getV()),0.5), + new ColorValue(new Color(270.0,color.getS(),color.getV()),0.75), + new ColorValue(new Color(360.0,color.getS(),color.getV()),1.0)}, ColorGradient.HSV); + sGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(color.getH(),0.0,color.getV()),0.0), + new ColorValue(new Color(color.getH(),1.0,color.getV()),1.0)}, ColorGradient.HSV); + vGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(color.getH(),color.getS(),0.0),0.0), + new ColorValue(new Color(color.getH(),color.getS(),1.0),1.0)}, ColorGradient.HSV); + } else { + rGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(0,0,0),0.0), + new ColorValue(new Color(255,0,0),1.0)}); + gGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(0,0,0),0.0), + new ColorValue(new Color(0,255,0),1.0)}); + bGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(0,0,0),0.0), + new ColorValue(new Color(0,0,255),1.0)}); + // hue is interpolated along the shortest route, using just 0 and 360 would result in constant color. + hGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(0.0,1.0,1.0),0.0), + new ColorValue(new Color(90.0,1.0,1.0),0.25), + new ColorValue(new Color(180.0,1.0,1.0),0.5), + new ColorValue(new Color(270.0,1.0,1.0),0.75), + new ColorValue(new Color(360.0,1.0,1.0),1.0)}, ColorGradient.HSV); + sGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(color.getH(),0.0,1.0),0.0), + new ColorValue(new Color(color.getH(),1.0,1.0),1.0)}, ColorGradient.HSV); + vGradient = new ColorGradient(new ColorValue[]{ + new ColorValue(new Color(color.getH(),1.0,0.0),0.0), + new ColorValue(new Color(color.getH(),1.0,1.0),1.0)}, ColorGradient.HSV); + } + colorGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getR(),color.getG(),color.getB()),0.0)}); } + private boolean internalUpdate = false; + private void updateWidgets() { + if (internalUpdate) + return; + internalUpdate = true; updateGradients(); rCanvas.setGradient(rGradient); gCanvas.setGradient(gGradient); bCanvas.setGradient(bGradient); + hCanvas.setGradient(hGradient); + sCanvas.setGradient(sGradient); + vCanvas.setGradient(vGradient); colorCanvas.setGradient(colorGradient); - rText.setText(Integer.toString(color.getR())); - gText.setText(Integer.toString(color.getG())); - bText.setText(Integer.toString(color.getB())); + + rCanvas.setPosition(color.getR()); + gCanvas.setPosition(color.getG()); + bCanvas.setPosition(color.getB()); + hCanvas.setPosition(color.getH()); + sCanvas.setPosition(color.getS()); + vCanvas.setPosition(color.getV()); + + if(!rText.isFocusControl()) + rText.setText(Integer.toString(color.getR())); + if(!gText.isFocusControl()) + gText.setText(Integer.toString(color.getG())); + if(!bText.isFocusControl()) + bText.setText(Integer.toString(color.getB())); + if(!hText.isFocusControl()) + hText.setText(Double.toString(color.getH())); + if(!sText.isFocusControl()) + sText.setText(Double.toString(color.getS())); + if(!vText.isFocusControl()) + vText.setText(Double.toString(color.getV())); + internalUpdate = false; + } + + private abstract static class IntGradientWidget extends GradientWidget { + + public IntGradientWidget(Composite parent, int style) { + super(parent, style); + } + + @Override + protected boolean updatePosition(double d) { + int value = (int)(d * 255); + if (value < 0) + value = 0; + if (value > 255) + value = 255; + updatePosition(value); + return true; + } + protected abstract void updatePosition(int value); + + protected void setPosition(int value) { + double d = ((double)value)/255.0; + setPosition(d); + } + + } + + private abstract static class GradientWidget extends ColorGradientCanvas { + + int pos = -1; + double posD = -1.0; + + int size = 8; + int sized2 = 4; + + + + public GradientWidget(Composite parent, int style) { + super(parent, style); + this.addMouseListener(new MouseListener() { + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 1) { + update(e); + } + } + + @Override + public void mouseUp(MouseEvent e) {} + @Override + public void mouseDoubleClick(MouseEvent e) {} + }); + this.addMouseMoveListener(new MouseMoveListener() { + + @Override + public void mouseMove(MouseEvent e) { + if ((e.stateMask & SWT.BUTTON1)>0) { + update(e); + } + } + }); + } + + protected void update(MouseEvent e) { + Rectangle r = getClientArea(); + double d; + if ((style & SWT.HORIZONTAL) > 0) { + d = (double)e.x / (double)r.width; + } else { + d = (double)e.y / (double)r.height; + } + if (d < 0.0) + d = 0.0; + else if (d > 1.0) + d = 1.0; + if (updatePosition(d)) { + if ((style & SWT.HORIZONTAL) > 0) { + pos = (int)(d*((double)r.width)); + } else { + pos = (int)(d*((double)r.height)); + } + posD = d; + } + } + + protected abstract boolean updatePosition(double d); + + protected void setPosition(double d) { + if (d == posD) + return; + _setPosition(d); + } + + private void _setPosition(double d) { + Rectangle r = getClientArea(); + posD = d; + if (posD >= 0.0) { + if ((style & SWT.HORIZONTAL) > 0) { + pos = (int)(d * (double)r.width); + } else { + pos = (int)(d * (double)r.height); + } + } else { + pos = -1; + } + } + + Rectangle prevClip; + @Override + protected void paintGradient(GC gc, Rectangle clip) { + super.paintGradient(gc, clip); + if (!clip.equals(prevClip)) { + prevClip = clip; + _setPosition(posD); + } + + if (pos >= 0) { + org.eclipse.swt.graphics.Color white = new org.eclipse.swt.graphics.Color(gc.getDevice(), 255,255,255); + org.eclipse.swt.graphics.Color black = new org.eclipse.swt.graphics.Color(gc.getDevice(), 0, 0, 0); + gc.setForeground(black); + gc.setBackground(white); + int x; + int y; + if ((style & SWT.HORIZONTAL) > 0) { + x = pos; + y = clip.height / 2; + } else { + x = clip.width / 2; + y = pos; + } + gc.fillOval(x-sized2, y-sized2, size, size); + gc.drawOval(x-sized2, y-sized2, size, size); + + white.dispose(); + black.dispose(); + } + } + } } diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorDialog.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorDialog.java new file mode 100644 index 000000000..195a16c65 --- /dev/null +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorDialog.java @@ -0,0 +1,84 @@ +package org.simantics.utils.ui.color; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Shell; + +public class ColorDialog extends Dialog{ + protected ColorComposite colorComposite; + protected Color value; + + public ColorDialog(Shell parentShell) { + super(parentShell); + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText("Color"); + } + + + public void setInitialValue(Color initialValue) { + this.value = new Color(initialValue); + } + + @Override + protected Point getInitialSize() { + return new Point(400, 400); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(1).margins(6, 0).applyTo(composite); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(composite); + colorComposite = new ColorComposite(composite, 0); + GridData childData = new GridData(GridData.FILL_BOTH); + colorComposite.setLayoutData(childData); + if (value != null) + colorComposite.setColor(value); + Group builtinGroup = new Group(composite, SWT.NONE); + builtinGroup.setText("System colors"); + GridLayoutFactory.fillDefaults().numColumns(8).spacing(2, 2).margins(4, 4).applyTo(builtinGroup); + for (int i = SWT.COLOR_WHITE; i <= SWT.COLOR_DARK_GRAY; i++) { + org.eclipse.swt.graphics.Color c = Display.getCurrent().getSystemColor(i); + Color color = new Color(c.getRGB()); + createColorButton(builtinGroup, color); + } + + + return composite; + } + + protected Button createColorButton(Composite parent, Color color) { + Button button = new Button(parent, SWT.PUSH); + button.setImage(ColorIconCreator.createImage(color, 32, SWT.BORDER)); + button.setData(color); + button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + colorComposite.setColor(color); + } + }); + return button; + } + + + public Color getColor() { + return colorComposite.getColor(); + } + + +} diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradient.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradient.java index 56195ce0c..5fc3d747c 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradient.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradient.java @@ -14,6 +14,7 @@ package org.simantics.utils.ui.color; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; @@ -266,7 +267,7 @@ public class ColorGradient { return type; } - public Collection getColorValues() { + public List getColorValues() { return values; } diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientAdjustingCanvas.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientAdjustingCanvas.java new file mode 100644 index 000000000..ea8983f3c --- /dev/null +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientAdjustingCanvas.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * 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.List; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +/** + * + * Canvas that shows color gradients and color positions. + * Without READ_ONLY style the canvas allows user to drag color positions. + * + * @author Marko Luukkainen + * + */ +public class ColorGradientAdjustingCanvas extends ColorGradientCanvas implements ISelectionProvider{ + + ColorValue[] values; + int last; + int coords[]; + int size = 8; + int sized2 = 4; + int width; + int height; + + int selectedValueIndex = -1; + + public ColorGradientAdjustingCanvas(Composite parent, int style) { + super(parent,style|SWT.DOUBLE_BUFFERED|SWT.NO_BACKGROUND); + addMouseListener(new MouseListener() { + + @Override + public void mouseUp(MouseEvent e) { + + } + + @Override + public void mouseDown(MouseEvent e) { + // store and reset selection + int prev = selectedValueIndex; + selectedValueIndex = -1; + + // locate closest ColorValue, and select it. + int d = Integer.MAX_VALUE; + for (int i = 0; i <= last; i++) { + int x = coords[i*2]; + int y = coords[i*2+1]; + int dx = Math.abs(x - e.x); + int dy = Math.abs(y - e.y); + if ((dx < size) && + (dy < size)) { + int dd = dx+dy; + if (dd < d) { + selectedValueIndex = i; + d = dd; + } + } + } + // if selection was changed, update it. + if (prev != selectedValueIndex) { + updateSelection(); + redraw(); + } + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + + } + }); + if ((style&SWT.READ_ONLY) == 0) { + addMouseMoveListener(new MouseMoveListener() { + + @Override + public void mouseMove(MouseEvent e) { + if ((e.stateMask & SWT.BUTTON1)>0) { + if (selectedValueIndex > 0 && selectedValueIndex < last) { + double d; + if ((ColorGradientAdjustingCanvas.this.style | SWT.HORIZONTAL) > 0) { + d = (double)e.x/(double)width; + } else { + d = (double)e.y/(double)height; + } + double r = max-min; + d *= r; + d += min; + double offset = r*0.015; + if (d <= values[selectedValueIndex-1].getValue()+offset) + d = values[selectedValueIndex-1].getValue()+offset; + else if (d >= values[selectedValueIndex+1].getValue()-offset) + d = values[selectedValueIndex+1].getValue()-offset; + values[selectedValueIndex]._setValue(d); + calculateCoords(width, height); + redraw(); + } + } + + } + }); + } + + } + + public void setGradient(ColorGradient gradient) { + int prevSize = 0; + if (values != null) + prevSize = values.length; + + values = gradient.getColorValueArray(); + last = values.length-1; + coords = new int[values.length*2]; + super.setGradient(gradient); + if (selectedValueIndex >= 0 && prevSize != values.length) { + selectedValueIndex = -1; + updateSelection(); + } else { + updateSelection(); + } + } + + double min; + double max; + + private void calculateCoords(int width, int height) { + this.width = width; + this.height = height; + + min = values[0].getValue(); + max = values[last].getValue(); + + if ((ColorGradientAdjustingCanvas.this.style & SWT.HORIZONTAL) > 0) { + for (int i = 0; i <= last ; i++) { + int y = height / 2; + double d = values[i].getValue(); + int x = (int)(((d-min)/(max-min))*(double)width); + coords[i*2] = x; + coords[i*2+1] = y; + } + } else { + for (int i = 0; i <= last ; i++) { + int x = width / 2; + double d = values[i].getValue(); + int y = (int)(((d-min)/(max-min))*(double)height); + coords[i*2] = x; + coords[i*2+1] = y; + } + } + } + + + @Override + protected void paintGradient(GC gc, Rectangle clip) { + if (values != null && values.length > 0) { + Image image = gradient.getGradientImage(clip.width,clip.height,ColorGradientAdjustingCanvas.this.style); + gc.drawImage(image, 0, 0); + image.dispose(); + calculateCoords(clip.width, clip.height); + + Color white = new Color(gc.getDevice(), 255,255,255); + Color yellow = new Color(gc.getDevice(), 255,230,0); + Color black = new Color(gc.getDevice(), 0, 0, 0); + for (int i = 0; i <= last ; i++) { + int x = coords[i*2]; + int y = coords[i*2+1]; + gc.setForeground(black); + if (selectedValueIndex == i) + gc.setBackground(yellow); + else + gc.setBackground(white); + if (i == 0 || i == last ) { + gc.fillRectangle(x-sized2, y-sized2, size, size); + gc.drawRectangle(x-sized2, y-sized2, size, size); + } else { + gc.fillOval(x-sized2, y-sized2, size, size); + gc.drawOval(x-sized2, y-sized2, size, size); + } + } + + white.dispose(); + black.dispose(); + yellow.dispose(); + } else { + gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + gc.fillRectangle(clip); + } + } + + private ISelection selection = new StructuredSelection(); + private List listeners = new ArrayList<>(); + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + listeners.add(listener); + } + + @Override + public ISelection getSelection() { + return selection; + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + listeners.remove(listener); + } + + @Override + public void setSelection(ISelection selection) { + ColorValue value = (ColorValue)((StructuredSelection)selection).getFirstElement(); + selectedValueIndex = gradient.getColorValues().indexOf(value); + } + + private void updateSelection() { + if (selectedValueIndex < 0) + selection = new StructuredSelection(); + else + selection = new StructuredSelection(values[selectedValueIndex]); + + for (ISelectionChangedListener l : listeners) { + l.selectionChanged(new SelectionChangedEvent(this, selection)); + } + } + + public int getPointSize() { + return size; + } + + public void setPointSize(int size) { + this.size = size; + this.sized2 = size/2; + } + + +} diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientCanvas.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientCanvas.java index 356de7ad0..27daeeb71 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientCanvas.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientCanvas.java @@ -33,29 +33,35 @@ public class ColorGradientCanvas extends Canvas{ int style; public ColorGradientCanvas(Composite parent, int style) { - // FIXME : use xor operation to get SWT.HORIZONTAL and SWT.VERTICAL - // out of style when it's passed to parent - super(parent,SWT.BORDER); + super(parent,(style|SWT.BORDER)&(~(SWT.VERTICAL|SWT.HORIZONTAL))); this.style = style & (SWT.VERTICAL | SWT.HORIZONTAL) ; - addPaintListener(new PaintListener() { + addPaintListener(createPaintListener()); + } + + public void setGradient(ColorGradient gradient) { + this.gradient = gradient; + this.redraw(); + } + + protected PaintListener createPaintListener() { + return new PaintListener() { public void paintControl(PaintEvent e) { GC gc = e.gc; Rectangle clip = gc.getClipping(); - if (gradient != null) { - Image image = gradient.getGradientImage(clip.width,clip.height,ColorGradientCanvas.this.style); - gc.drawImage(image, 0, 0); - image.dispose(); - } else { - gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); - gc.fillRectangle(clip); - } + paintGradient(gc, clip); } - }); + }; } - public void setGradient(ColorGradient gradient) { - this.gradient = gradient; - this.redraw(); - } + protected void paintGradient(GC gc, Rectangle clip) { + if (gradient != null) { + Image image = gradient.getGradientImage(clip.width,clip.height,ColorGradientCanvas.this.style); + gc.drawImage(image, 0, 0); + image.dispose(); + } else { + gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + gc.fillRectangle(clip); + } + } } diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite.java index f8a52bc70..77b7668d1 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite.java @@ -43,7 +43,6 @@ 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; @@ -272,7 +271,7 @@ public class ColorGradientComposite extends Composite implements ISelectionChang public void dispose() { for (Image i : images.values()) i.dispose(); - + super.dispose(); } public void setGradient(ColorGradient gradient) { diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite2.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite2.java new file mode 100644 index 000000000..497407bca --- /dev/null +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorGradientComposite2.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * 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 org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +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.Composite; + +/** + * Widget to create and edit color gradients + * + * @author Marko Luukkainen + * + */ +public class ColorGradientComposite2 extends Composite implements ISelectionChangedListener{ + + private ColorGradientAdjustingCanvas gradientComposite; + 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(); + + public ColorGradientComposite2(Composite parent, int style) { + super(parent,style); + GridLayout layout = new GridLayout(2,false); + this.setLayout(layout); + gradientComposite = new ColorGradientAdjustingCanvas(this,SWT.HORIZONTAL); + + gradientComposite.addSelectionChangedListener(this); + + 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"); + + Composite buttonComposite = new Composite(this,SWT.NONE); + + buttonComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + + 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) { + RGB rgb = openDialog(null); + 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)gradientComposite.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) { + + Object obj = ((StructuredSelection)gradientComposite.getSelection()).getFirstElement(); + if (obj == null) + return; + if (obj instanceof ColorValue) { + RGB rgb = openDialog(((ColorValue)obj).getColor().getRgb()); + 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) { + Object o = ((StructuredSelection)gradientComposite.getSelection()).getFirstElement(); + if (o == null) + return; + if (o instanceof ColorValue) { + 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(false, true).align(SWT.LEFT, SWT.FILL).applyTo(typeComposite); + GridDataFactory.fillDefaults().span(1, 1).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(buttonComposite); + } + + protected RGB openDialog(RGB selection) { +// ColorDialog dialog = new ColorDialog(ColorGradientComposite2.this.getShell(), SWT.NONE); +// if (selection != null) +// dialog.setRGB(selection); +// RGB rgb = dialog.open(); +// return rgb; + org.simantics.utils.ui.color.ColorDialog dialog = new org.simantics.utils.ui.color.ColorDialog(getShell()); + if (selection != null) { + Color color = new Color(selection); + dialog.setInitialValue(color); + } + if (dialog.open() == org.simantics.utils.ui.color.ColorDialog.OK) { + Color color = dialog.getColor(); + return color.getRgb(); + } + return null; + } + + public void addColor(Color color, double value) { + addColor(new ColorValue(color,value)); + } + + public void addColor(ColorValue value) { + values.add(value); + updateWidgets(); + } + + private void updateWidgets() { + Collections.sort(values,new ColorValueComparator()); + gradientComposite.setGradient(new ColorGradient(values,type)); + } + + public ColorGradient getGradient() { + return new ColorGradient(values,type); + } + + public void dispose() { + super.dispose(); + } + + public void setGradient(ColorGradient gradient) { + values.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 void modifyColorValueColor(ColorValue cValue, RGB rgb) { + values.remove(cValue);; + Color newColor = new Color(rgb.red,rgb.green,rgb.blue); + ColorValue newCValue = new ColorValue(newColor,cValue.getValue()); + values.add(newCValue); + 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); + int index = values.indexOf(obj); + if (index > 0 && index < values.size() -1) + removeButton.setEnabled(true); + else + removeButton.setEnabled(false); + } + } + +} + + diff --git a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorValue.java b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorValue.java index be6355790..80f41c8a3 100644 --- a/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorValue.java +++ b/bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/color/ColorValue.java @@ -40,6 +40,10 @@ public class ColorValue { return value; } + public void _setValue(double value) { + this.value = value; + } + @Override public int hashCode() { return color.hashCode() ^ new Double(value).hashCode();