]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Editing of texts inside SVG elements 99/899/7
authorAntti Villberg <antti.villberg@semantum.fi>
Fri, 1 Sep 2017 10:57:05 +0000 (13:57 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Mon, 4 Sep 2017 19:18:44 +0000 (22:18 +0300)
refs #7458

Change-Id: Ied21d5c7706f2fa7c060088b5b57c46ee17977b2

39 files changed:
bundles/org.simantics.diagram.ontology/graph/Diagram.pgraph
bundles/org.simantics.diagram.ontology/graph/DiagramElements.pgraph
bundles/org.simantics.diagram/adapters.xml
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/AnimatedSVGElementClassFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DefinedElementFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/DefinedElementHandler.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ElementFactoryUtil.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ElementParameterRequest.java [new file with mode: 0644]
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/SVGElementClassFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/AnimatedSVGImage.java [moved from bundles/org.simantics.g2d/src/org/simantics/g2d/svg/AnimatedSVGImage.java with 97% similarity]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/AnimatedSVGNode.java [moved from bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/AnimatedSVGNode.java with 97% similarity]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/DecorationSVGNode.java [moved from bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/DecorationSVGNode.java with 60% similarity]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorState.java [new file with mode: 0644]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateManager.java [new file with mode: 0644]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateStatic.java [new file with mode: 0644]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGHolderNode.java [moved from bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGHolderNode.java with 99% similarity]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGImage.java [new file with mode: 0644]
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGNode.java [new file with mode: 0644]
bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagSceneGraph.java
bundles/org.simantics.diagram/src/org/simantics/diagram/profile/ButtonNode.java
bundles/org.simantics.diagram/src/org/simantics/diagram/profile/IconNode.java
bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementHints.java
bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementUtils.java
bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/Parameters.java [new file with mode: 0644]
bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/impl/DefaultParameters.java [new file with mode: 0644]
bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/AnimatedNode.java [new file with mode: 0644]
bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/PlainElementPropertySetter.java
bundles/org.simantics.g2d/src/org/simantics/g2d/image/impl/ImageURLFactory.java
bundles/org.simantics.g2d/src/org/simantics/g2d/svg/SVGImage.java
bundles/org.simantics.modeling.ui/plugin.xml
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/DocumentDecorationStyle.java
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/IssueDecorationStyle.java
bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java
bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/example/SampleThread.java
bundles/org.simantics.scenegraph/META-INF/MANIFEST.MF
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGNode.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SingleElementNode.java
bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/BundleModuleSource.java
bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/BundleUtils.java

index bd99e8f988a2ba46cdef5d31a74cb0b0de9c45c4..42aa3b0ef905524aedd53cb00e6bcc93edc45517 100644 (file)
@@ -114,11 +114,17 @@ DIA.Functions.activeProfileModifier : L0.Function
     L0.HasValueType "String -> String"
 DIA.Functions.diagramElementIssuePath : L0.Function
 
+DIA.DefinedElement.Parameter <T L0.PropertyRelation <T L0.FunctionalRelation
+
 DIA.DefinedElement
   // RequiresValueType ]is omitted during transition period.
   // Used to be ==> "[String]" but DIA.symbolCode now returns either "[String]" or "[G2DNodeModification]" directly
   >-- DIA.symbolCode <R L0.HasProperty : L0.FunctionalRelation
   >-- DIA.symbolDropHandler ==> "[WorkbenchSelectionElement] -> <WriteGraph,Proc> ()" <R L0.HasProperty : L0.FunctionalRelation
+  // Parameters are id of edited element and new text.
+  >-- DIA.DefinedElement.textEditor ==> "String -> String -> <Proc> ()" <R L0.HasProperty : DIA.DefinedElement.Parameter
+  // This function gives initial text for the text editor. Parameter is the id of the edited element.
+  >-- DIA.DefinedElement.textEditorFullText ==> "String -> <Proc> String" <R L0.HasProperty : DIA.DefinedElement.Parameter 
 
 DIA.DiagramActivityCondition <T L0.Entity
   --> DIA.DiagramActivityCondition.test ==> "Resource -> <ReadGraph> String" <R L0.HasProperty : L0.FunctionalRelation 
index bc95fb7b64fc0042b2e3b6ba2791dcc62b6434dd..7fb684aefb49b564b31f8436d901e6338c573019 100644 (file)
@@ -16,6 +16,8 @@ DIA.RasterElement <T DIA.Element
 
 DIA.SVGElement <T DIA.Element
     @L0.singleProperty G2D.HasSVGDocument
+      // Parameters are id of edited element and new text.
+    >-- DIA.SVGElement.enableEditing ==> "Boolean" <R L0.HasProperty : DIA.DefinedElement.Parameter
 
 //// ANIMATED SVG ELEMENT
 
index 24552f5a280a8d8e1e383741c39701999c8ba0f2..04e85be36d8e054741368037bee0ee367d4bdf98 100644 (file)
                <type uri="http://www.simantics.org/Diagram-0.0/Scenegraph/SVGImage" class="org.simantics.scenegraph.loader.StandardScenegraphLoader">
                        <this/>
                        <bundle />
-                       <string>org.simantics.scenegraph.g2d.nodes.SVGNode</string>
+                       <string>org.simantics.diagram.elements.SVGNode</string>
                </type>
                <type uri="http://www.simantics.org/Diagram-0.0/Scenegraph/Composite" class="org.simantics.scenegraph.loader.StandardScenegraphLoader">
                        <this/>
index 76159f6ad3319f9377f7ef0f46412fa62722f6a6..cbeed5b2ad852eb2311687a821c1ffbef245d0b1 100644 (file)
@@ -21,6 +21,7 @@ import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
 import org.simantics.db.common.procedure.guarded.GuardedAsyncProcedureWrapper;
 import org.simantics.db.common.request.BinaryAsyncRead;
 import org.simantics.db.procedure.AsyncProcedure;
+import org.simantics.diagram.elements.AnimatedSVGImage;
 import org.simantics.diagram.stubs.G2DResource;
 import org.simantics.diagram.synchronization.SynchronizationHints;
 import org.simantics.diagram.synchronization.graph.TransformSynchronizer;
@@ -40,7 +41,6 @@ import org.simantics.g2d.elementclass.ImageClass.ImageElementHandler;
 import org.simantics.g2d.image.DefaultImages;
 import org.simantics.g2d.image.Image;
 import org.simantics.g2d.image.ProviderUtils;
-import org.simantics.g2d.svg.AnimatedSVGImage;
 import org.simantics.layer0.Layer0;
 import org.simantics.utils.datastructures.cache.IFactory;
 import org.simantics.utils.datastructures.cache.ProvisionException;
index e7bb472e4ed610450bdbb19a62df5be9f30d67de..9f02393c7dd90cc30b2ec1d73757329c862b4d15 100644 (file)
@@ -37,6 +37,7 @@ import org.simantics.g2d.diagram.IDiagram;
 import org.simantics.g2d.element.ElementClass;
 import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.impl.DefaultParameters;
 import org.simantics.g2d.element.handler.impl.DefaultTransform;
 import org.simantics.g2d.element.handler.impl.ObjectTerminal;
 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
@@ -228,6 +229,7 @@ public class DefinedElementFactory extends ElementFactoryAdapter {
                                             TextImpl.INSTANCE,
                                             new StaticObjectAdapter(elementType),
                                             DefaultTransform.INSTANCE,
+                                            DefaultParameters.INSTANCE,
                                             StaticSymbolImageInitializer.INSTANCE,
                                             new StaticSymbolImpl(img),
                                             DefinedElementHandler.INSTANCE,
@@ -257,6 +259,8 @@ public class DefinedElementFactory extends ElementFactoryAdapter {
         // This is needed for terminal tooltips.
         e.setHint(TooltipParticipant.TOOLTIP_KEY, TerminalTooltipProvider.INSTANCE);
 
+        ElementFactoryUtil.readParameters(graph, element, e);
+
         GuardedAsyncProcedureWrapper<IElement> guard = new GuardedAsyncProcedureWrapper<IElement>(procedure, 1);
         ElementFactoryUtil.readTransform(graph, element, e, guard);
 
index 17f22cfcfae23d131531913ed526d9b1e5b84061..e3e1269f37e0b9e29feb577a1d82160da88e1dd4 100644 (file)
@@ -18,6 +18,7 @@ import org.simantics.g2d.image.Image;
 import org.simantics.scenegraph.Node;
 import org.simantics.scenegraph.g2d.G2DParentNode;
 import org.simantics.scenegraph.g2d.IG2DNode;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 
 public class DefinedElementHandler implements SceneGraph, InternalSize, Resize, Outline {
@@ -52,6 +53,9 @@ public class DefinedElementHandler implements SceneGraph, InternalSize, Resize,
                        n.setTransform(at); // FIXME: not tested..
                }
         }
+        if(parent instanceof SingleElementNode) {
+            ((SingleElementNode)parent).setParameters(ElementUtils.getParameters(e));
+        }
     }
 
     public void cleanup(final IElement e) {
index 89ee666bb21b92e75519a3df2724a674dad938a9..f05667cb6882b73f6faab486473f9ec836e9e98d 100644 (file)
 package org.simantics.diagram.adapter;
 
 import java.awt.geom.AffineTransform;
+import java.util.Map;
 
 import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;
+import org.simantics.db.common.procedure.adapter.ProcedureAdapter;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.procedure.AsyncProcedure;
 import org.simantics.diagram.stubs.DiagramResource;
@@ -59,6 +61,15 @@ public class ElementFactoryUtil {
         procedure.execute(graph, e);
     }
 
+    public static void readParameters(AsyncReadGraph graph, final Resource resource, final IElement e) {
+        graph.asyncRequest(new ElementParameterRequest(resource), new ProcedureAdapter<Map<String,Object>>() {
+            @Override
+            public void execute(Map<String, Object> result) {
+                ElementUtils.setParameters(e, result);
+            }
+        });
+    }
+
     public static ISynchronizationContext getContext(IDiagram diagram) {
         return diagram.getHint(SynchronizationHints.CONTEXT);
     }
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ElementParameterRequest.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/ElementParameterRequest.java
new file mode 100644 (file)
index 0000000..6592ec2
--- /dev/null
@@ -0,0 +1,35 @@
+package org.simantics.diagram.adapter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.diagram.stubs.DiagramResource;
+
+class ElementParameterRequest extends ResourceRead<Map<String,Object>> {
+
+       protected ElementParameterRequest(Resource resource) {
+               super(resource);
+       }
+
+       @Override
+       public Map<String, Object> perform(ReadGraph graph) throws DatabaseException {
+               Variable var = Variables.getPossibleVariable(graph, resource);
+               if(var == null) return null;
+               Map<String,Object> result = null;
+               for(Variable property : var.getProperties(graph, DiagramResource.URIs.DefinedElement_Parameter)) {
+                       Object value = property.getPossibleValue(graph);
+                       if(value != null) {
+                               if(result == null) result = new HashMap<>();
+                               result.put(property.getName(graph), value);
+                       }
+               }
+               return result;
+       }
+
+}
\ No newline at end of file
index 94d783f2dfcaf4d78a00b2fe2ec1d68f25a747fb..4f4c7f6ba67bde016a39274a1eacbeb167308a37 100644 (file)
@@ -26,6 +26,7 @@ import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.common.utils.NameUtils;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.procedure.AsyncProcedure;
+import org.simantics.diagram.elements.SVGImage;
 import org.simantics.diagram.stubs.G2DResource;
 import org.simantics.diagram.synchronization.SynchronizationHints;
 import org.simantics.diagram.synchronization.graph.TransformSynchronizer;
@@ -45,7 +46,6 @@ import org.simantics.g2d.elementclass.ImageClass.ImageElementHandler;
 import org.simantics.g2d.image.DefaultImages;
 import org.simantics.g2d.image.Image;
 import org.simantics.g2d.image.ProviderUtils;
-import org.simantics.g2d.svg.SVGImage;
 import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.utils.Development;
 import org.simantics.utils.datastructures.cache.IFactory;
similarity index 97%
rename from bundles/org.simantics.g2d/src/org/simantics/g2d/svg/AnimatedSVGImage.java
rename to bundles/org.simantics.diagram/src/org/simantics/diagram/elements/AnimatedSVGImage.java
index afbc586863918897adc56770787554c6ed8d71b5..2f9490b6e1d7d91e83c04ceac05bd7e3b0d1b577 100644 (file)
@@ -9,7 +9,7 @@
  * Contributors:
  *     VTT Technical Research Centre of Finland - initial API and implementation
  *******************************************************************************/
-package org.simantics.g2d.svg;
+package org.simantics.diagram.elements;
 
 import java.awt.Point;
 import java.awt.Shape;
@@ -19,8 +19,6 @@ import java.util.EnumSet;
 import org.simantics.g2d.image.Image;
 import org.simantics.scenegraph.Node;
 import org.simantics.scenegraph.g2d.G2DParentNode;
-import org.simantics.scenegraph.g2d.nodes.AnimatedSVGNode;
-import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.utils.datastructures.cache.IFactory;
 import org.simantics.utils.datastructures.cache.ProvisionException;
 
similarity index 97%
rename from bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/AnimatedSVGNode.java
rename to bundles/org.simantics.diagram/src/org/simantics/diagram/elements/AnimatedSVGNode.java
index 1d0c9f795f81d8404caf4be42974c3bf26fa2230..e4924d1b1061b2d38786f01b9644e4480ea8c1ee 100644 (file)
@@ -9,7 +9,7 @@
  * Contributors:
  *     VTT Technical Research Centre of Finland - initial API and implementation
  *******************************************************************************/
-package org.simantics.scenegraph.g2d.nodes;
+package org.simantics.diagram.elements;
 
 import java.awt.Graphics2D;
 import java.awt.geom.Rectangle2D;
@@ -24,6 +24,7 @@ import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
 import javax.script.ScriptException;
 
+import org.simantics.g2d.elementclass.AnimatedNode;
 import org.simantics.scenegraph.utils.BufferedImage;
 import org.simantics.scenegraph.utils.G2DUtils;
 import org.simantics.scenegraph.utils.MipMapBufferedImage;
@@ -36,7 +37,7 @@ import com.kitfox.svg.SVGException;
 import com.kitfox.svg.SVGUniverse;
 import com.kitfox.svg.xml.StyleAttribute;
 
-public class AnimatedSVGNode extends SVGNode {
+public class AnimatedSVGNode extends SVGNode implements AnimatedNode {
     /**
      * 
      */
similarity index 60%
rename from bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/DecorationSVGNode.java
rename to bundles/org.simantics.diagram/src/org/simantics/diagram/elements/DecorationSVGNode.java
index 7aac233dec1022e1320d168c49711f9a0812f11f..4e3b7fe00f3d5900fec026a68cb711dc3ae2491c 100644 (file)
@@ -1,5 +1,6 @@
-package org.simantics.scenegraph.g2d.nodes;
+package org.simantics.diagram.elements;
 
+import org.simantics.scenegraph.g2d.nodes.Decoration;
 
 public class DecorationSVGNode extends SVGNode implements Decoration {
 
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorState.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorState.java
new file mode 100644 (file)
index 0000000..0c13750
--- /dev/null
@@ -0,0 +1,358 @@
+package org.simantics.diagram.elements;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
+
+import com.kitfox.svg.Group;
+import com.kitfox.svg.Line;
+import com.kitfox.svg.Rect;
+import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.Text;
+import com.kitfox.svg.Tspan;
+import com.kitfox.svg.animation.AnimationElement;
+
+/**
+ * @author Antti Villberg
+ * @since 1.31.0
+ */
+class EditorState {
+
+       enum ModificationClass {
+               SINGLE_INSERT, AREA_INSERT,SINGLE_DELETE,AREA_DELETE,NO_EDIT
+       }
+
+       EditorStateStatic base;
+       ModificationClass modificationClass = ModificationClass.NO_EDIT;
+       int caretPosition = -1;
+       int selectionOtherPosition = -1;
+       String currentText = null;
+
+       private String selectedText() {
+               if(editModeHasSelection()) {
+                       int min = Math.min(caretPosition, selectionOtherPosition);
+                       int max = Math.max(caretPosition, selectionOtherPosition);
+                       return currentText.substring(min, max);
+               }
+               return null;
+       }
+
+       private boolean editModeHasSelection() {
+               return selectionOtherPosition != -1;
+       }
+
+       private void editModeClearSelection() {
+               selectionOtherPosition = -1;
+       }
+
+       private void deleteCurrentSelection() {
+               int min = Math.min(caretPosition, selectionOtherPosition);
+               int max = Math.max(caretPosition, selectionOtherPosition);
+               currentText = currentText.substring(0, min) + currentText.substring(max, currentText.length());
+               caretPosition = min;
+               editModeClearSelection();
+       }
+
+       public void applyEditMode(SVGDiagram diagram) throws SVGException {
+
+               Text text = (Text)diagram.getElement(base.textElementId);
+               Tspan span = (Tspan)text.getContent().get(0);
+
+               // Measure the X-dimensions of the font - append TERM_STRING to account for trailing whitespace
+               span.setText(currentText + EditorStateManager.TERM_STRING);
+               text.rebuild();
+               diagram.updateTime(0);
+               double textWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
+
+               // Measure the caret position
+               span.setText(currentText.substring(0, caretPosition) + EditorStateManager.TERM_STRING);
+               text.rebuild();
+               diagram.updateTime(0);
+               double caretRectWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
+
+               double selectionOtherWidth = 0;
+               if(selectionOtherPosition != -1) {
+                       span.setText(currentText.substring(0, selectionOtherPosition) +  EditorStateManager.TERM_STRING);
+                       text.rebuild();
+                       diagram.updateTime(0);
+                       selectionOtherWidth = text.getBoundingBox().getWidth() - base.termStringWidth;
+               }
+
+
+               // Finally the actual text
+               span.setText(currentText);
+               text.rebuild();
+               diagram.updateTime(0);
+               Rectangle2D finalBB = text.getBoundingBox();
+
+               Group group = (Group)text.getParent();
+               Line line = new Line();
+               try { 
+
+                       group.removeChild(text);
+
+                       double xPadding = 0;
+
+                       double minY = (base.verticalDimensions.getMinY()-1);
+                       double height = (base.verticalDimensions.getHeight()+2);
+
+                       Rect rect = new Rect();
+                       rect.addAttribute("x", AnimationElement.AT_XML, "" + (finalBB.getMinX()-xPadding));
+                       rect.addAttribute("y", AnimationElement.AT_XML, "" + minY);
+                       rect.addAttribute("width", AnimationElement.AT_XML, "" + (textWidth+xPadding));
+                       rect.addAttribute("height", AnimationElement.AT_XML, "" + height);
+                       rect.addAttribute("fill", AnimationElement.AT_XML, "#ccc");
+                       group.loaderAddChild(null, rect);
+
+                       double caretX = finalBB.getMinX() + caretRectWidth;
+
+                       if(selectionOtherPosition != -1) {
+                               double selectionX = finalBB.getMinX() + selectionOtherWidth;
+                               Rect selection = new Rect();
+                               if(selectionOtherPosition < caretPosition) {
+                                       selection.addAttribute("x", AnimationElement.AT_XML, "" + selectionX);
+                                       selection.addAttribute("y", AnimationElement.AT_XML, "" + minY);
+                                       selection.addAttribute("width", AnimationElement.AT_XML, "" + (caretX-selectionX));
+                                       selection.addAttribute("height", AnimationElement.AT_XML, "" + height);
+                                       selection.addAttribute("fill", AnimationElement.AT_XML, "#888");
+                               } else {
+                                       selection.addAttribute("x", AnimationElement.AT_XML, "" + caretX);
+                                       selection.addAttribute("y", AnimationElement.AT_XML, "" + minY);
+                                       selection.addAttribute("width", AnimationElement.AT_XML, "" + (selectionX-caretX));
+                                       selection.addAttribute("height", AnimationElement.AT_XML, "" + height);
+                                       selection.addAttribute("fill", AnimationElement.AT_XML, "#888");
+                               }
+                               group.loaderAddChild(null, selection);
+                       }
+
+                       line.addAttribute("x1", AnimationElement.AT_XML, "" + caretX);
+                       line.addAttribute("x2", AnimationElement.AT_XML, "" + caretX);
+                       line.addAttribute("y1", AnimationElement.AT_XML, "" + (base.verticalDimensions.getMinY()-1));
+                       line.addAttribute("y2", AnimationElement.AT_XML, "" + (base.verticalDimensions.getMaxY()+1));
+                       line.addAttribute("stroke", AnimationElement.AT_XML, "black");
+                       line.addAttribute("stroke-width", AnimationElement.AT_XML, "0.5");
+                       group.loaderAddChild(null, line);
+
+                       group.loaderAddChild(null, text);
+
+               } finally {
+
+               }
+
+               diagram.updateTime(0);
+
+       }
+
+       boolean keyPressed(EditorStateManager esm, KeyPressedEvent e) {
+               boolean result = keyPressedInternal(esm, e);
+               if(selectionOtherPosition == caretPosition)
+                       editModeClearSelection();
+               return result;
+       }
+
+       private void performDelete() {
+               if(editModeHasSelection()) {
+                       deleteCurrentSelection();
+                       modificationClass = ModificationClass.AREA_DELETE;
+               } else {
+                       if(caretPosition < currentText.length()) {
+                               currentText = currentText.substring(0, caretPosition) + currentText.substring(caretPosition+1, currentText.length());
+                       }
+                       modificationClass = ModificationClass.SINGLE_DELETE;
+               }
+       }
+
+       private void performCopy() {
+               String selection = selectedText();
+               if(selection == null) return;
+               Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+               clipboard.setContents(new StringSelection(selection), null);
+       }
+
+       boolean keyPressedInternal(EditorStateManager esm, KeyPressedEvent e) {
+
+               if(e.keyCode == java.awt.event.KeyEvent.VK_BACK_SPACE) {
+                       if(editModeHasSelection()) {
+                               deleteCurrentSelection();
+                               modificationClass = ModificationClass.AREA_DELETE;
+                       } else {
+                               if(caretPosition > 0) {
+                                       currentText = currentText.substring(0, caretPosition-1) + currentText.substring(caretPosition, currentText.length());
+                                       caretPosition--;
+                               }
+                               modificationClass = ModificationClass.SINGLE_DELETE;
+                       }
+               } else if (java.awt.event.KeyEvent.VK_DELETE == e.keyCode) {
+                       performDelete();
+               } else if (java.awt.event.KeyEvent.VK_C == e.keyCode && e.isControlDown()) {
+                       performCopy();
+                       return false;
+               } else if (java.awt.event.KeyEvent.VK_X == e.keyCode && e.isControlDown()) {
+                       performCopy();
+                       performDelete();
+               } else if (java.awt.event.KeyEvent.VK_V == e.keyCode && e.isControlDown()) {
+                       Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+                       DataFlavor dataFlavor = DataFlavor.stringFlavor;
+                       if (clipboard.isDataFlavorAvailable(dataFlavor)) {
+                               try {
+                                       String text = clipboard.getData(dataFlavor).toString();
+                                       if(editModeHasSelection())
+                                               deleteCurrentSelection();
+
+                                       currentText = currentText.substring(0, caretPosition) + text + currentText.substring(caretPosition, currentText.length());
+                                       caretPosition += text.length();
+                                       modificationClass = ModificationClass.AREA_INSERT;
+                               } catch (UnsupportedFlavorException | IOException e1) {
+                               }
+                       } else {
+                               return false;
+                       }
+               } else if (java.awt.event.KeyEvent.VK_A == e.keyCode && e.isControlDown()) {
+                       caretPosition = 0;
+                       selectionOtherPosition = currentText.length();
+               } else if (java.awt.event.KeyEvent.VK_Z == e.keyCode && e.isControlDown()) {
+                       esm.undo();
+                       return false;
+               } else if (java.awt.event.KeyEvent.VK_Y == e.keyCode && e.isControlDown()) {
+                       esm.redo();
+                       return false;
+               } else if (java.awt.event.KeyEvent.VK_ESCAPE == e.keyCode) {
+                       esm.deactivateEdit();
+               } else if (java.awt.event.KeyEvent.VK_LEFT == e.keyCode) {
+                       if(!e.isShiftDown() && editModeHasSelection()) {
+                               if(selectionOtherPosition < caretPosition) {
+                                       caretPosition = selectionOtherPosition;
+                               }
+                               editModeClearSelection();
+                       } else {
+                               if(e.isShiftDown() && !editModeHasSelection()) {
+                                       selectionOtherPosition = caretPosition;
+                               }
+                               if(caretPosition > 0) {
+                                       caretPosition--;
+                               }
+                       }
+               } else if (java.awt.event.KeyEvent.VK_RIGHT == e.keyCode) {
+                       if(!e.isShiftDown() && editModeHasSelection()) {
+                               if(selectionOtherPosition > caretPosition) {
+                                       caretPosition = selectionOtherPosition;
+                               }
+                               editModeClearSelection();
+                       } else {
+                               if(e.isShiftDown() && !editModeHasSelection()) {
+                                       selectionOtherPosition = caretPosition;
+                               }
+                               if(caretPosition < currentText.length()) {
+                                       caretPosition++;
+                               }
+                       }
+               } else if (java.awt.event.KeyEvent.VK_END == e.keyCode) {
+                       if(e.isShiftDown()) {
+                               if(!editModeHasSelection()) {
+                                       selectionOtherPosition = caretPosition;
+                               }
+                       } else {
+                               editModeClearSelection();
+                       }
+                       caretPosition = currentText.length();
+               } else if (java.awt.event.KeyEvent.VK_HOME == e.keyCode) {
+                       if(e.isShiftDown()) {
+                               if(!editModeHasSelection()) {
+                                       selectionOtherPosition = caretPosition;
+                               }
+                       } else {
+                               editModeClearSelection();
+                       }
+                       caretPosition = 0;
+               } else if (java.awt.event.KeyEvent.VK_ENTER == e.keyCode) {
+                       esm.applyEdit();
+                       esm.deactivateEdit();
+               } else if(isAllowedCharacter(e)) {
+                       if(editModeHasSelection())
+                               deleteCurrentSelection();
+                       currentText = currentText.substring(0, caretPosition) + e.character + currentText.substring(caretPosition, currentText.length());
+                       caretPosition++;
+                       modificationClass = ModificationClass.SINGLE_INSERT;
+               } else {
+                       return false;
+               }
+
+               esm.paint();
+
+               return true;
+
+       }
+
+       void replace(EditorState other) {
+               base = other.base;
+               caretPosition = other.caretPosition;
+               currentText = other.currentText;
+               selectionOtherPosition = other.selectionOtherPosition;
+       }
+
+       boolean shouldReplace(EditorState comparedTo) {
+               return modificationClass.equals(comparedTo.modificationClass);
+       }
+
+       EditorState copy() {
+               EditorState result = new EditorState();
+               result.replace(this);
+               return result;
+       }
+
+       private boolean isAllowedCharacter(KeyPressedEvent e) {
+               char c = e.character;
+               if (c == 65535 || Character.getType(c) == Character.CONTROL) {
+                       return false;
+               }
+               return true;
+       }
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((base == null) ? 0 : base.hashCode());
+               result = prime * result + caretPosition;
+               result = prime * result + ((currentText == null) ? 0 : currentText.hashCode());
+               result = prime * result + ((modificationClass == null) ? 0 : modificationClass.hashCode());
+               result = prime * result + selectionOtherPosition;
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               EditorState other = (EditorState) obj;
+               if (base == null) {
+                       if (other.base != null)
+                               return false;
+               } else if (!base.equals(other.base))
+                       return false;
+               if (caretPosition != other.caretPosition)
+                       return false;
+               if (currentText == null) {
+                       if (other.currentText != null)
+                               return false;
+               } else if (!currentText.equals(other.currentText))
+                       return false;
+               if (modificationClass != other.modificationClass)
+                       return false;
+               if (selectionOtherPosition != other.selectionOtherPosition)
+                       return false;
+               return true;
+       }
+
+}
\ No newline at end of file
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateManager.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateManager.java
new file mode 100644 (file)
index 0000000..8ddb16a
--- /dev/null
@@ -0,0 +1,258 @@
+package org.simantics.diagram.elements;
+
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.simantics.db.common.utils.Logger;
+import org.simantics.diagram.elements.EditorState.ModificationClass;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.element.IElement;
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scl.runtime.function.Function1;
+import org.simantics.scl.runtime.function.Function2;
+
+import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.Text;
+import com.kitfox.svg.Tspan;
+import com.kitfox.svg.animation.AnimationElement;
+
+/**
+ * @author Antti Villberg
+ * @since 1.31.0
+ */
+class EditorStateManager {
+
+       static String TERM_STRING = "-----";
+       static String EDITOR_CLASS = "edit";
+       static String EDITOR_ID = "edit";
+       private SVGNode node;
+
+       private LinkedList<EditorState> editorState = null;
+       private int editorStateIndex = 0;
+
+       EditorStateManager(SVGNode node) {
+               this.node = node;
+       }
+
+       public boolean isEditMode() {
+               return editorState != null;
+       }
+
+       public EditorState currentState() {
+               return editorState.get(editorStateIndex);
+       }
+
+       public int currentHash() {
+               if(!isEditMode()) return 0;
+               return currentState().hashCode();
+       }
+
+       public void activateEditMode(SVGDiagram diagram, Text text) {
+
+               if(isEditMode()) return;
+
+               if(text.getId().length() == 0) return;
+
+               EditorState es = new EditorState();
+               es.base = new EditorStateStatic();
+               es.base.textElementId = text.getId();
+
+               Tspan span = (Tspan)text.getContent().get(0);
+               String currentText = span.getText();
+
+               SingleElementNode sne = node.getSingleElementNode();
+               Function1<String,String> fullTextFunction = sne.getParameter("textEditorFullText"); 
+               if(fullTextFunction != null)
+                       es.currentText = fullTextFunction.apply(es.base.textElementId);
+               if(es.currentText == null) {
+                       es.currentText = currentText;
+               }
+
+               es.caretPosition = es.currentText.length();
+               es.selectionOtherPosition = 0;
+
+               // Measure the Y-dimensions of the font
+               try {
+                       span.setText("Ig");
+                       text.rebuild();
+                       diagram.updateTime(0);
+                       es.base.verticalDimensions = text.getBoundingBox(); 
+                       span.setText(TERM_STRING);
+                       text.rebuild();
+                       diagram.updateTime(0);
+                       es.base.termStringWidth = text.getBoundingBox().getWidth(); 
+                       span.setText(currentText);
+                       text.rebuild();
+                       diagram.updateTime(0);
+               } catch (SVGException e) {
+                       e.printStackTrace();
+               }
+
+               ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(node);
+               IElement ie = DiagramNodeUtil.getElement(ctx, sne);
+
+               EditDataNode data = EditDataNode.getNode(node);
+               deactivateEdit(data, null);
+               TextEditActivation result = new TextEditActivation(0, ie, ctx);
+               data.setTextEditActivation(result);
+
+               editorState = new LinkedList<>();
+               editorState.push(es);
+               editorStateIndex = 0;
+
+               paint();
+
+       }
+
+       private TextEditActivation editActivation;
+
+       void applyEdit() {
+               SingleElementNode sne = node.getSingleElementNode();
+               EditorState es = currentState();
+               Function2<String,String,Object> editor = sne.getParameter("textEditor");
+               if(editor != null) {
+                       editor.apply(es.base.textElementId, es.currentText);
+               }
+       }
+
+       protected boolean deactivateEdit() {
+               boolean result = deactivateEdit( editActivation );
+               result |= editActivation != null;
+               editActivation = null;
+               editorState = null;
+               paint();
+               return result;
+       }
+
+       protected boolean deactivateEdit(TextEditActivation activation) {
+               return deactivateEdit( EditDataNode.getNode(node), activation );
+       }
+
+       protected boolean deactivateEdit(EditDataNode data, TextEditActivation activation) {
+               TextEditActivation previous = data.getTextEditActivation();
+               if (previous != null && (previous == activation || activation == null)) {
+                       previous.release();
+                       data.setTextEditActivation(null);
+                       return true;
+               }
+               return false;
+       }
+
+       protected boolean keyPressed(KeyPressedEvent e) {
+               if(isEditMode()) {
+                       EditorState es = currentState();
+                       EditorState nes = es.copy();
+                       if(nes.keyPressed(this, e)) {
+                               if(!isEditMode()) {
+                                       // This key actually terminated editing
+                                       return true;
+                               }
+                               if(nes.shouldReplace(es)) {
+                                       es.replace(nes);
+                               } else {
+                                       while(editorState.size() > (editorStateIndex + 1))
+                                               editorState.removeLast();
+                                       editorState.add(nes);
+                                       editorStateIndex = editorState.size() - 1;
+                               }
+                               return true; 
+                       }
+               }
+               return false;
+       }
+
+
+       public boolean tryToStartEditMode(SVGDiagram diagram) {
+               SVGElement element = diagram.getElement(EDITOR_ID);
+               if(element != null && element instanceof Text) {
+                       activateEditMode(diagram, (Text)element);
+                       return true;
+               }
+               return false;
+       }
+
+       public boolean tryToStartEditMode(SVGDiagram diagram, MouseClickEvent e) {
+
+               if(diagram != null) {
+
+                       Point2D local = node.controlToLocal( e.controlPosition );
+                       // FIXME: once the event coordinate systems are cleared up, remove this workaround
+                       local = node.parentToLocal(local);
+
+                       double tolerance = 2.0;
+                       Rectangle2D pickRect = new Rectangle2D.Double(local.getX()-tolerance, local.getY()-tolerance, 2*tolerance, 2*tolerance); 
+
+                       try {
+                               List<?> retVec = diagram.pick(pickRect, null);
+                               for(int i=0;i<retVec.size();i++) {
+                                       List<?> l = (List<?>)retVec.get(i);
+                                       for(int j=0;j<l.size();j++) {
+                                               SVGElement element = (SVGElement)l.get(j);      
+                                               if(element instanceof Tspan) {
+                                                       return true;
+                                               }
+                                               if(element instanceof Text) {
+                                                       Text text = (Text)element;
+                                                       if(text.hasAttribute("class", AnimationElement.AT_XML)) {
+                                                               String clazz = text.getPresAbsolute("class").getStringValue();
+                                                               if(clazz.contains(EDITOR_CLASS)) {
+                                                                       activateEditMode(diagram, text);
+                                                                       return true;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+
+                       } catch (SVGException e1) {
+                               Logger.defaultLogError(e1);
+                       }
+               }
+
+               return false;
+
+       }
+
+       boolean applyEditMode(SVGDiagram diagram) throws SVGException {
+
+               if(isEditMode()) {
+                       EditorState es = currentState();
+                       es.applyEditMode(diagram);
+                       return true;
+               }
+
+               return false;
+
+       }
+
+       void paint() {
+               node.cleanDiagramCache();
+               node.repaint();
+       }
+
+       void undo() {
+               while(editorStateIndex > 0 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
+                       editorStateIndex--;
+               }
+               if(editorStateIndex > 0)
+                       editorStateIndex--;
+               paint();
+       }
+
+       void redo() {
+               while(editorStateIndex < editorState.size() - 1 && currentState().modificationClass.equals(ModificationClass.NO_EDIT)) {
+                       editorStateIndex++;
+               }
+               if(editorStateIndex < editorState.size() - 1) {
+                       editorStateIndex++;
+               }
+               paint();
+       }
+
+}
\ No newline at end of file
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateStatic.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/EditorStateStatic.java
new file mode 100644 (file)
index 0000000..f7e5791
--- /dev/null
@@ -0,0 +1,51 @@
+package org.simantics.diagram.elements;
+
+import java.awt.geom.Rectangle2D;
+
+/**
+ * @author Antti Villberg
+ * @since 1.31.0
+ */
+class EditorStateStatic {
+
+       Rectangle2D verticalDimensions = null;
+       double termStringWidth = 0;
+       String textElementId = null;
+
+       @Override
+       public int hashCode() {
+               final int prime = 31;
+               int result = 1;
+               result = prime * result + ((textElementId == null) ? 0 : textElementId.hashCode());
+               result = prime * result + ((verticalDimensions == null) ? 0 : verticalDimensions.hashCode());
+               long temp;
+               temp = Double.doubleToLongBits(termStringWidth);
+               result = prime * result + (int) (temp ^ (temp >>> 32));
+               return result;
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (this == obj)
+                       return true;
+               if (obj == null)
+                       return false;
+               if (getClass() != obj.getClass())
+                       return false;
+               EditorStateStatic other = (EditorStateStatic) obj;
+               if (textElementId == null) {
+                       if (other.textElementId != null)
+                               return false;
+               } else if (!textElementId.equals(other.textElementId))
+                       return false;
+               if (verticalDimensions == null) {
+                       if (other.verticalDimensions != null)
+                               return false;
+               } else if (!verticalDimensions.equals(other.verticalDimensions))
+                       return false;
+               if (Double.doubleToLongBits(termStringWidth) != Double.doubleToLongBits(other.termStringWidth))
+                       return false;
+               return true;
+       }
+
+}
\ No newline at end of file
similarity index 99%
rename from bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/SVGHolderNode.java
rename to bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGHolderNode.java
index dbf6b755947a0b898ad2aa88b5cd01f3028dffda..ec26e3ab9f2ae905ef54c0e545b2e9493ed00555 100644 (file)
@@ -9,7 +9,7 @@
  * Contributors:
  *     VTT Technical Research Centre of Finland - initial API and implementation
  *******************************************************************************/
-package org.simantics.scenegraph.g2d.nodes;
+package org.simantics.diagram.elements;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -17,6 +17,7 @@ import java.awt.Graphics2D;
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
+
 import org.simantics.scenegraph.g2d.G2DParentNode;
 import org.simantics.scenegraph.g2d.IG2DNode;
 
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGImage.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGImage.java
new file mode 100644 (file)
index 0000000..839642d
--- /dev/null
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * 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.diagram.elements;
+
+import java.awt.Point;
+
+import org.simantics.g2d.image.Image;
+import org.simantics.scenegraph.Node;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.utils.datastructures.cache.IFactory;
+import org.simantics.utils.datastructures.cache.ProvisionException;
+
+/**
+ * This is a SVG implementation to the PaintableSymbol interface.
+ */
+public class SVGImage extends org.simantics.g2d.svg.SVGImage {
+
+    public SVGImage(String nodeIdentifier, String document, Point targetSize) {
+        super(nodeIdentifier, document, targetSize);
+    }
+
+    public SVGImage(String nodeIdentifier, String document) {
+        super(nodeIdentifier, document);
+       }
+
+    public static IFactory<Image> createFactoryFromString(String nodeIdentifier, String svgDocument, Point targetSize) {
+        return new SVGFactory(nodeIdentifier, svgDocument, targetSize);
+    }
+
+    static class SVGFactory implements IFactory<Image> {
+        String nodeIdentifier;
+        String document;
+        Point targetSize;
+        public SVGFactory(String nodeIdentifier, String document) {
+            this(nodeIdentifier, document, null);
+        }
+        public SVGFactory(String nodeIdentifier, String document, Point referenceSize) {
+            if (nodeIdentifier == null)
+                throw new NullPointerException("nodeIdentifier is null");
+            if (document == null)
+                throw new NullPointerException("document is null");
+
+            this.nodeIdentifier = nodeIdentifier;
+            this.document = document;
+            this.targetSize = referenceSize;
+        }
+        @Override
+        public Image get() throws ProvisionException {
+            return new SVGImage(nodeIdentifier, document, targetSize);
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (!obj.getClass().equals(getClass()))
+                return false;
+
+            SVGFactory other = (SVGFactory)obj;
+
+            if (!nodeIdentifier.equals(other.nodeIdentifier))
+                return false;
+            if (targetSize != null) {
+                if (!targetSize.equals(other.targetSize))
+                    return false;
+            } else {
+                if (other.targetSize != null)
+                    return false;
+            }
+
+            return document.equals(other.document);
+        }
+        @Override
+        public int hashCode() {
+            return nodeIdentifier.hashCode() * 31 + document.hashCode() + 123;
+        }
+    }
+
+    @Override
+    public Node init(G2DParentNode parent) {
+        // FIXME: mipmaps enabled here by default, since some apps just don't work without them.
+        // Figure out a way to pass the mipmap argument from above
+
+        SVGNode node = parent.getOrCreateNode(nodeIdentifier, SVGNode.class);
+        node.setData(svgDocument);
+        node.setTargetSize(targetSize);
+        node.useMipMap(true);
+
+        return node;
+    }
+
+}
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGNode.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/SVGNode.java
new file mode 100644 (file)
index 0000000..8084c2a
--- /dev/null
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * 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.diagram.elements;
+
+import java.awt.Graphics2D;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.participant.Selection;
+import org.simantics.g2d.element.IElement;
+import org.simantics.scenegraph.ExportableWidget.RasterOutputWidget;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.IG2DNode;
+import org.simantics.scenegraph.g2d.events.EventTypes;
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;
+import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
+import org.simantics.scenegraph.utils.NodeUtil;
+
+import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGException;
+
+@RasterOutputWidget
+public class SVGNode extends org.simantics.scenegraph.g2d.nodes.SVGNode {
+
+    private static final long serialVersionUID = 4735066193941274186L;
+
+    transient EditorStateManager esm  = null;
+    transient boolean parametersProcessed = false;
+
+    @Override
+    public void cleanup() {
+        if (esm != null)
+            removeEventHandler(this);
+        super.cleanup();
+    }
+
+    @Override
+    public void render(Graphics2D g2d) {
+        if (!parametersProcessed) {
+            SingleElementNode sne = getSingleElementNode();
+            Boolean enableEditing = sne.getParameter("enableEditing");
+            if (enableEditing != null && enableEditing) {
+                esm = new EditorStateManager(this);
+                addEventHandler(this);
+            }
+            parametersProcessed = true;
+        }
+
+        super.render(g2d);
+    }
+
+    @Override
+    protected int dynamicHash() {
+        return esm != null ? esm.currentHash() : 0;
+    }
+
+    @Override
+    protected boolean applyAssignments(SVGDiagram diagram, List<SVGNodeAssignment> assignments) throws SVGException {
+        boolean changed = super.applyAssignments(diagram, assignments);
+        if (esm != null && !esm.applyEditMode(diagram))
+            diagram.updateTime(0);
+        return changed;
+    }
+
+    private boolean isSelected(IG2DNode node) {
+        ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(this);
+        IElement ie = DiagramNodeUtil.getElement(ctx, node);
+        if (ie == null)
+            return false;
+        Selection sel = ctx.getAtMostOneItemOfClass(Selection.class);
+        if (sel != null) {
+            Set<IElement> elems = sel.getSelection(0);
+            if (elems.size() == 1 && elems.contains(ie))
+                return true;
+        }
+        return false;
+    }
+
+    private boolean isSelected() {
+        SingleElementNode sne = getSingleElementNode();
+        if (isSelected(sne))
+            return true;
+        INode n = sne.getParent();
+        while (n != null) {
+            if (isSelected((IG2DNode)n))
+                return true;
+            n = n.getParent();
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean mouseClicked(MouseClickEvent e) {
+        if(esm != null) {
+            if(esm.isEditMode()) {
+                esm.applyEdit();
+                esm.deactivateEdit();
+                return true;
+            }
+            if(isSelected()) {
+                if(esm.tryToStartEditMode(diagramCache, e))
+                    return true;
+            }
+        }
+        return super.mouseClicked(e);
+    }
+
+    SingleElementNode getSingleElementNode() {
+        ICanvasContext ctx = DiagramNodeUtil.getCanvasContext(this);
+        // FIXME: needed only because eventdelegator registrations are done before adding node to scene graph.
+        if (ctx == null)
+            return null;
+        return (SingleElementNode)NodeUtil.getNearestParentOfType(this, SingleElementNode.class);
+    }
+
+    @Override
+    protected boolean keyPressed(KeyPressedEvent e) {
+        if (esm != null && esm.keyPressed(e))
+            return true;
+        return super.keyPressed(e);
+    }
+
+    @Override
+    protected boolean handleCommand(CommandEvent e) {
+        if (esm != null
+                && !esm.isEditMode()
+                && "org.eclipse.ui.edit.rename".equals(e.command.id)
+                && isSelected()
+                && esm.tryToStartEditMode(diagramCache)) {
+            return true;
+        }
+        return super.handleCommand(e);
+    }
+
+    @Override
+    public int getEventMask() {
+        return EventTypes.KeyPressedMask | EventTypes.MouseClickMask | EventTypes.CommandMask;
+    }
+
+}
index 9895dd39f018c90e277a3c543e39efbb2775f9f4..3f0587f1d663e3dc4031fa4a278744df6d8dd8cb 100644 (file)
@@ -9,6 +9,7 @@ import java.awt.geom.Rectangle2D;
 import org.simantics.databoard.util.Bean;
 import org.simantics.diagram.adapter.FlagTextInfo;
 import org.simantics.diagram.adapter.SVGImageInfo;
+import org.simantics.diagram.elements.SVGNode;
 import org.simantics.diagram.elements.TextNode;
 import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
@@ -22,7 +23,6 @@ import org.simantics.g2d.utils.Alignment;
 import org.simantics.scenegraph.Node;
 import org.simantics.scenegraph.g2d.G2DParentNode;
 import org.simantics.scenegraph.g2d.nodes.FlagNode;
-import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.ui.colors.Colors;
 import org.simantics.ui.fonts.Fonts;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
index 2ad90acbe26fa59ad41799f9dcc654947f9886cd..96d42474b023f68487edf835845c9e78f5e2920c 100644 (file)
@@ -8,6 +8,7 @@ import java.awt.geom.Rectangle2D;
 import org.simantics.datatypes.literal.RGB;
 import org.simantics.datatypes.literal.Vec2d;
 import org.simantics.diagram.elements.DiagramNodeUtil;
+import org.simantics.diagram.elements.SVGNode;
 import org.simantics.diagram.internal.Activator;
 import org.simantics.diagram.profile.ButtonResult.A;
 import org.simantics.diagram.profile.ButtonResult.B;
@@ -16,7 +17,6 @@ import org.simantics.scenegraph.g2d.events.EventTypes;
 import org.simantics.scenegraph.g2d.events.MouseEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
-import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.scl.runtime.function.Function1;
 
index 94c99741ded57e1ecc2fb56f06c28b996c1b97ca..9521cec7502183575978db13473df44432b29773 100644 (file)
@@ -6,10 +6,10 @@ import java.awt.geom.Rectangle2D;
 
 import org.simantics.datatypes.literal.RGB;
 import org.simantics.datatypes.literal.Vec2d;
+import org.simantics.diagram.elements.SVGNode;
 import org.simantics.diagram.internal.Activator;
 import org.simantics.diagram.profile.IconResult.A;
 import org.simantics.diagram.profile.IconResult.B;
-import org.simantics.scenegraph.g2d.nodes.SVGNode;
 
 public class IconNode extends IconButtonNode {
        
index 9710fab683b6ea7ce48a5b03013a7189248bf220..dc8f914748d0f661d2a2c9925d78fdb29e8e1fa0 100644 (file)
@@ -20,14 +20,15 @@ import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 import org.simantics.g2d.canvas.Hints;
 import org.simantics.g2d.connection.ConnectionEntity;
 import org.simantics.g2d.connection.IConnectionAdvisor;
-import org.simantics.g2d.element.handler.SceneGraph;
 import org.simantics.g2d.element.handler.EdgeVisuals.ArrowType;
 import org.simantics.g2d.element.handler.EdgeVisuals.StrokeType;
+import org.simantics.g2d.element.handler.SceneGraph;
 import org.simantics.g2d.image.Image;
 import org.simantics.g2d.utils.Alignment;
 import org.simantics.scenegraph.Node;
@@ -56,6 +57,11 @@ public class ElementHints {
      */
     public static final Key KEY_TRANSFORM = new KeyOf(AffineTransform.class, "TRANSFORM");
 
+    /**
+     * For describing representation parameters of an element.
+     */
+    public static final Key KEY_PARAMETERS = new KeyOf(Map.class, "PARAMETERS");
+
     /**
      * For defining the rectangular boundaries of an element.
      */
index 74934321739daa9dc59e20ffa0f02a96ab25b65f..300e74d31e75e04ee7ecf750ab0b2e58ba513951 100644 (file)
@@ -49,6 +49,7 @@ import org.simantics.g2d.element.handler.Hover;
 import org.simantics.g2d.element.handler.InternalSize;
 import org.simantics.g2d.element.handler.Move;
 import org.simantics.g2d.element.handler.Outline;
+import org.simantics.g2d.element.handler.Parameters;
 import org.simantics.g2d.element.handler.Parent;
 import org.simantics.g2d.element.handler.Pick;
 import org.simantics.g2d.element.handler.Resize;
@@ -350,6 +351,18 @@ public class ElementUtils {
         e.getElementClass().getSingleItem(Transform.class).setTransform(e, at);
     }
 
+    public static void setParameters(IElement e, Map<String,Object> parameters)
+    {
+        Parameters ps = e.getElementClass().getSingleItem(Parameters.class);
+        if(ps != null) ps.setParameters(e, parameters);
+    }
+
+    public static Map<String,Object> getParameters(IElement e)
+    {
+        Parameters ps = e.getElementClass().getAtMostOneItemOfClass(Parameters.class);
+        return ps != null ? ps.getParameters(e) : null;
+    }
+
     public static AffineTransform getInvTransform(IElement e)
     {
         try {
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/Parameters.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/Parameters.java
new file mode 100644 (file)
index 0000000..c903e43
--- /dev/null
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.element.handler;
+
+import java.util.Map;
+
+import org.simantics.g2d.element.IElement;
+
+/**
+ * @author Antti Villberg
+ * @since 1.31.0
+ */
+public interface Parameters extends ElementHandler {
+
+    /**
+     * @return A map of element parameters. A <code>null</code> value is allowed
+     *         which indicates no parameters.
+     */
+    Map<String,Object> getParameters(IElement e);
+
+    /**
+     * @param p
+     *            the parameter map for an element. A <code>null</code> value is
+     *            allowed which indicates no parameters.
+     */
+    void setParameters(IElement e, Map<String,Object> p);
+
+}
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/impl/DefaultParameters.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/handler/impl/DefaultParameters.java
new file mode 100644 (file)
index 0000000..1b7b453
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.element.handler.impl;
+
+import java.util.Map;
+
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.Parameters;
+
+/**
+ * @author Antti Villberg
+ * @since 1.31.0
+ */
+public class DefaultParameters implements Parameters {
+
+       private static final long serialVersionUID = -9138310692519364097L;
+
+       public static final DefaultParameters INSTANCE = new DefaultParameters();
+
+       @Override
+       public Map<String, Object> getParameters(IElement e) {
+               return e.getHint(ElementHints.KEY_PARAMETERS);
+       }
+
+       @Override
+       public void setParameters(IElement e, Map<String, Object> p) {
+               if (p != null) {
+                       e.setHint(ElementHints.KEY_PARAMETERS, p);
+               } else {
+                       e.removeHint(ElementHints.KEY_PARAMETERS);
+               }
+       }
+
+}
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/AnimatedNode.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/AnimatedNode.java
new file mode 100644 (file)
index 0000000..4bdd534
--- /dev/null
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2017 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.elementclass;
+
+/**
+ * @author Antti Villberg
+ * @since 1.31.0
+ */
+public interface AnimatedNode {
+}
index 2500787369959c9a0a99ca3143ed90e08a425b07..f009dcfac41183900e81f4c2bf71ebadf2fcd509 100644 (file)
@@ -19,7 +19,6 @@ import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.Node;
 import org.simantics.scenegraph.g2d.G2DParentNode;
 import org.simantics.scenegraph.g2d.IG2DNode;
-import org.simantics.scenegraph.g2d.nodes.AnimatedSVGNode;
 import org.simantics.scenegraph.utils.NodeUtil;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
@@ -84,7 +83,7 @@ public class PlainElementPropertySetter implements PropertySetter {
     }
 
     private Node findAnimatedNode(Node node) {
-        Node animated = findSingle(node, AnimatedSVGNode.class);
+        Node animated = findSingle(node, AnimatedNode.class);
         if(animated != null) return animated;
         else return node;
     }
index bd3d3e56959e914fd7aeb570eac15fc4a0dec28f..3cea842cd3d1a8202be3ca312ad041a27655d55a 100644 (file)
@@ -13,6 +13,7 @@ package org.simantics.g2d.image.impl;
 
 import java.awt.image.BufferedImage;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 
 import javax.imageio.ImageIO;
@@ -40,7 +41,14 @@ public class ImageURLFactory implements IFactory<Image> {
         if (url==null) throw new IllegalArgumentException("null arg");
         this.url = url;
     }
+    
+    public static Image loadFromURL(String nodeIdentifier, URL url) throws IOException {
+        try (InputStream in = url.openStream()) {
+            return new SVGImage(nodeIdentifier, in);
+        }
+    }
 
+    
     @Override
     public Image get() throws ProvisionException {
         boolean svg = url.getFile().toLowerCase().endsWith(".svg");
index 54ceeec4185547aebf86f304659d31895777295c..e8d2456c1127d2c21810263d8997d73f94e01d74 100644 (file)
@@ -36,16 +36,13 @@ public class SVGImage implements Image {
     static EnumSet<Feature> caps = EnumSet.of(Feature.Vector);
 
     private Rectangle2D bounds;
-    private final String nodeIdentifier;
-    private final String svgDocument;
-    private Point targetSize;
+    protected final String nodeIdentifier;
+    protected final String svgDocument;
+    protected Point targetSize;
 
     public static SVGImage loadFromURL(String nodeIdentifier, URL url) throws IOException {
-        InputStream in = url.openStream();
-        try {
+        try (InputStream in = url.openStream()) {
             return new SVGImage(nodeIdentifier, in);
-        } finally {
-            in.close();
         }
     }
 
index 363cb9894e7ccebd2ab0b19e15ef04e58fda17c9..578c752ba37504fe7e6b46024d17081cdeb3829a 100644 (file)
          <activeWhen>
             <with
                   variable="activeContexts">
+            <and>
                <test
                      property="org.simantics.ui.tester.canUndo">
                </test>
+               <iterate
+                     ifEmpty="false"
+                     operator="and">
+                  <not>
+                     <equals
+                           value="org.simantics.modeling.ui.diagramming.inlineEdit">
+                     </equals>
+                  </not>
+               </iterate>
+            </and>
             </with>
          </activeWhen>
       </handler>
          <activeWhen>
             <with
                   variable="activeContexts">
+            <and>
                <test
                      property="org.simantics.ui.tester.canRedo">
                </test>
+               <iterate
+                     ifEmpty="false"
+                     operator="and">
+                  <not>
+                     <equals
+                           value="org.simantics.modeling.ui.diagramming.inlineEdit">
+                     </equals>
+                  </not>
+               </iterate>
+            </and>
             </with>
          </activeWhen>
       </handler>
index e2d0a2b1306d1b6940289b9bf5f2d6773f9db3c6..bb626c8641259959d11fbd94c7a832a6303e5c92 100644 (file)
@@ -20,6 +20,8 @@ import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.variable.Variable;
+import org.simantics.diagram.elements.DecorationSVGNode;
+import org.simantics.diagram.elements.SVGNode;
 import org.simantics.diagram.profile.StyleBase;
 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
 import org.simantics.document.DocumentResource;
@@ -27,8 +29,6 @@ import org.simantics.modeling.ModelingResources;
 import org.simantics.modeling.ui.Activator;
 import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.g2d.nodes.Decoration;
-import org.simantics.scenegraph.g2d.nodes.DecorationSVGNode;
-import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.scenegraph.profile.EvaluationContext;
 import org.simantics.scenegraph.profile.common.ProfileVariables;
 import org.simantics.scenegraph.utils.NodeUtil;
index c1f7cc648e54b38ff9386d33bf877a42717826f2..0200af96e520bc48835859c163f73145092b1358 100644 (file)
@@ -25,6 +25,8 @@ import org.simantics.db.common.procedure.adapter.TransientCacheListener;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.db.layer0.variable.Variables;
+import org.simantics.diagram.elements.DecorationSVGNode;
+import org.simantics.diagram.elements.SVGNode;
 import org.simantics.diagram.profile.StyleBase;
 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
 import org.simantics.issues.Severity;
@@ -35,8 +37,6 @@ import org.simantics.modeling.ui.Activator;
 import org.simantics.modeling.ui.diagram.style.IssueDecorationStyle.IssueResult;
 import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.g2d.nodes.Decoration;
-import org.simantics.scenegraph.g2d.nodes.DecorationSVGNode;
-import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.scenegraph.profile.EvaluationContext;
 import org.simantics.scenegraph.profile.common.ProfileVariables;
 import org.simantics.scenegraph.utils.NodeUtil;
index dd09f9ebf6ced3a93018fe3ecafb8f141dbdd067..09999bdd2faccee53b2b4969016756a343ccfa8c 100644 (file)
@@ -36,6 +36,7 @@ import org.simantics.db.Resource;
 import org.simantics.db.common.request.UnaryRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.exception.RuntimeDatabaseException;
+import org.simantics.diagram.elements.DecorationSVGNode;
 import org.simantics.diagram.elements.DiagramNodeUtil;
 import org.simantics.diagram.elements.TextGridNode;
 import org.simantics.diagram.elements.TextNode;
@@ -61,7 +62,6 @@ import org.simantics.scenegraph.g2d.nodes.BackgroundNode;
 import org.simantics.scenegraph.g2d.nodes.BoundsNode;
 import org.simantics.scenegraph.g2d.nodes.ConnectionNode;
 import org.simantics.scenegraph.g2d.nodes.DataNode;
-import org.simantics.scenegraph.g2d.nodes.DecorationSVGNode;
 import org.simantics.scenegraph.g2d.nodes.NavigationNode;
 import org.simantics.scenegraph.g2d.nodes.SVGNode;
 import org.simantics.scenegraph.g2d.nodes.SelectionNode;
index 67b785f2579443edf334ca8029b00e052055df67..46494549b56275a905b6d0dcc4e9973c37cabbcf 100644 (file)
@@ -23,8 +23,8 @@ import java.util.List;
 
 import javax.swing.BorderFactory;
 import javax.swing.JSlider;
+
 import org.simantics.scenegraph.g2d.G2DSceneGraph;
-import org.simantics.scenegraph.g2d.nodes.AnimatedSVGNode;
 import org.simantics.scenegraph.g2d.nodes.GridNode;
 import org.simantics.scenegraph.g2d.nodes.NavigationNode;
 import org.simantics.scenegraph.g2d.nodes.RulerNode;
@@ -85,38 +85,38 @@ public class SampleThread implements Runnable {
                grid.setZIndex(1);
                
 //             AnimatedSVGNode bmw = null;
-               AnimatedSVGNode svg1 = null;
-               AnimatedSVGNode svg2 = null;
-               AnimatedSVGNode svg3 = null;
-               AnimatedSVGNode svg4 = null;
-               try {
-                       String data = loadSVG("tynnyri.svg");
-                       svg1 = nav.addNode(AnimatedSVGNode.class);
-                       svg1.setZIndex(20);
-                       svg1.setData(data);
-                       svg1.setTransform(AffineTransform.getTranslateInstance(0, 150));
-                       svg1.useMipMap(false);
-                       
-                       svg2 = nav.addNode(AnimatedSVGNode.class);
-                       svg2.setZIndex(20);
-                       svg2.setData(data);
-                       svg2.setTransform(AffineTransform.getTranslateInstance(250, 150));
-                       svg2.useMipMap(false);
-                       
-                       svg3 = nav.addNode(AnimatedSVGNode.class);
-                       svg3.setZIndex(20);
-                       svg3.setData(data);
-                       svg3.setTransform(AffineTransform.getTranslateInstance(500, 150));
-                       svg3.useMipMap(false);
-                       
-                       svg4 = nav.addNode(AnimatedSVGNode.class);
-                       svg4.setZIndex(20);
-                       svg4.setData(data);
-                       svg4.setTransform(AffineTransform.getTranslateInstance(750, 150));
-                       svg4.useMipMap(false);
-               } catch(IOException e) {
-                       e.printStackTrace();
-               }
+//             AnimatedSVGNode svg1 = null;
+//             AnimatedSVGNode svg2 = null;
+//             AnimatedSVGNode svg3 = null;
+//             AnimatedSVGNode svg4 = null;
+//             try {
+//                     String data = loadSVG("tynnyri.svg");
+//                     svg1 = nav.addNode(AnimatedSVGNode.class);
+//                     svg1.setZIndex(20);
+//                     svg1.setData(data);
+//                     svg1.setTransform(AffineTransform.getTranslateInstance(0, 150));
+//                     svg1.useMipMap(false);
+//                     
+//                     svg2 = nav.addNode(AnimatedSVGNode.class);
+//                     svg2.setZIndex(20);
+//                     svg2.setData(data);
+//                     svg2.setTransform(AffineTransform.getTranslateInstance(250, 150));
+//                     svg2.useMipMap(false);
+//                     
+//                     svg3 = nav.addNode(AnimatedSVGNode.class);
+//                     svg3.setZIndex(20);
+//                     svg3.setData(data);
+//                     svg3.setTransform(AffineTransform.getTranslateInstance(500, 150));
+//                     svg3.useMipMap(false);
+//                     
+//                     svg4 = nav.addNode(AnimatedSVGNode.class);
+//                     svg4.setZIndex(20);
+//                     svg4.setData(data);
+//                     svg4.setTransform(AffineTransform.getTranslateInstance(750, 150));
+//                     svg4.useMipMap(false);
+//             } catch(IOException e) {
+//                     e.printStackTrace();
+//             }
 
 //             try {
 //                     bmw = nav.addNode(AnimatedSVGNode.class);
@@ -296,6 +296,7 @@ public class SampleThread implements Runnable {
                                t2 = (float)v/1000;
                        }});
        
+       /*
                svg1.setScript(script1);
                svg2.setScript(script1);
 
@@ -321,7 +322,8 @@ public class SampleThread implements Runnable {
                                e.printStackTrace();
                        }
                }
-               
+               */
+       
                close();
        }
        
index 610990a02a02e11a465ea9ca6935926f390c4349..e6218a1368d9d6aa48ed136cd031a1d44a95fc45 100644 (file)
@@ -22,6 +22,7 @@ Bundle-ClassPath: .,
  lib/batik-awt-util-1.8.jar,
  lib/batik-util-1.8.jar
 Export-Package: com.kitfox.svg,
+ com.kitfox.svg.animation,
  com.kitfox.svg.xml,
  org.simantics.scenegraph,
  org.simantics.scenegraph.adapters,
index ec85fd75c86e7893a8b7341bcfde6641e380d244..db491a3fda07e3145d91a38bdbbf8e693595debf 100644 (file)
@@ -24,6 +24,7 @@ import java.net.URL;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -64,16 +65,21 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     protected Point           targetSize       = null;
     protected Boolean         useMipMap        = true;
     protected Rectangle2D     bounds           = null;
-    
+
     protected List<SVGNodeAssignment> assignments = new ArrayList<SVGNodeAssignment>();
 
-    transient BufferedImage buffer    = null;
-    transient String documentCache    = null;
-    transient SVGDiagram diagramCache = null;
-    transient String dataHash         = null;
+    protected transient BufferedImage buffer    = null;
+    protected transient String documentCache    = null;
+    protected transient SVGDiagram diagramCache = null;
+    protected transient String dataHash         = null;
 
     static transient Map<String, WeakReference<BufferedImage>> bufferCache = new HashMap<String, WeakReference<BufferedImage>>();
 
+    @Override
+    public void init() {
+        super.init();
+    }
+
     @Override
     public void cleanup() {
         cleanDiagramCache();
@@ -159,6 +165,10 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
             g2d.setTransform(ot);
     }
 
+    protected int dynamicHash() {
+        return 0;
+    }
+
     protected String parseSVG() {
         if (data == null)
             return null;
@@ -173,7 +183,6 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
                     diagramCache = null;
                 }
 
-                
                 // Lets check for rootAssignment that contributes the whole SVG 
                 SVGNodeAssignment rootAssignment = null;
                 if (!assignments.isEmpty()) {
@@ -191,7 +200,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
                     // NOTE: hard-coded to assume all SVG data is encoded in UTF-8
                     dataBytes = data.getBytes("UTF-8");
                 }
-                dataHash = digest(dataBytes, assignments);
+                dataHash = digest(dataBytes, assignments, dynamicHash());
                 URI uri = univ.loadSVG(new ByteArrayInputStream(dataBytes), dataHash);
                 diagramCache = univ.getDiagram(uri, false);
 
@@ -208,7 +217,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
                         bbox = root.getBoundingBox();
                         if (bbox.isEmpty()) {
                             // Lets check if this should be visible or not
-                            Set presentationAttributes = root.getPresentationAttributes();
+                            Set<?> presentationAttributes = root.getPresentationAttributes();
                             if (!presentationAttributes.contains("display")) {
                                 // TODO: fix this - How can one read values of attributes in SVG salamander???
                                 univ.decRefCount(diagramCache.getXMLBase());
@@ -246,14 +255,13 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         return dataHash;
     }
 
-    private static boolean applyAssignments(SVGDiagram diagram, List<SVGNodeAssignment> assignments) throws SVGException {
+    protected boolean applyAssignments(SVGDiagram diagram, List<SVGNodeAssignment> assignments) throws SVGException {
         if (assignments.isEmpty())
             return false;
+
         boolean changed = false;
+
         for (SVGNodeAssignment ass : assignments) {
-//             System.err.println("assign: " + ass.elementId + " " + ass.attributeNameOrId + " " + ass.value);
-//             if("opacity".equals(ass.attributeNameOrId))
-//                     System.err.println("faaf");
             SVGElement e = diagram.getElement(ass.elementId);
             if (e != null) {
                 if ("$text".equals(ass.attributeNameOrId)) {
@@ -269,21 +277,28 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
                             ((Text) parent).rebuild();
                         changed = true;
                     }
+                } else if (ass.attributeNameOrId.startsWith("#")) {
+                    e.setAttribute(ass.attributeNameOrId.substring(1), AnimationElement.AT_CSS, ass.value);
+                    changed = true;
                 } else {
                     e.setAttribute(ass.attributeNameOrId, AnimationElement.AT_AUTO, ass.value);
                     changed = true;
                 }
             }
         }
-        diagram.updateTime(0);
+
         return changed;
     }
 
     public static Rectangle2D getBounds(String data) {
-        return getBounds(data, null);
+        return getBounds(data, 0);
+    }
+
+    public static Rectangle2D getBounds(String data, int dynamicHash) {
+        return getBounds(data, Collections.emptyList(), dynamicHash);
     }
 
-    public static Rectangle2D getBounds(String data, List<SVGNodeAssignment> assignments) {
+    public static Rectangle2D getBounds(String data, List<SVGNodeAssignment> assignments, int dynamicHash) {
         if (data == null) {
             new Exception("null SVG data").printStackTrace();
             return null;
@@ -293,7 +308,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         try {
             // NOTE: hard-coded to assume all SVG data is encoded in UTF-8
             byte[] dataBytes = data.getBytes("UTF-8");
-            String digest = digest(dataBytes, assignments);
+            String digest = digest(dataBytes, assignments, dynamicHash);
 
             SVGUniverse univ = SVGCache.getSVGUniverse();
             // TODO: this completely removes any parallel processing from the SVG loading which would be nice to have.
@@ -327,10 +342,10 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     }
 
     public static Rectangle2D getRealBounds(String data) {
-       return getRealBounds(data, null);
+        return getRealBounds(data, Collections.emptyList(), 0);
     }
 
-    public static Rectangle2D getRealBounds(String data, List<SVGNodeAssignment> assignments) {
+    public static Rectangle2D getRealBounds(String data, List<SVGNodeAssignment> assignments, int dynamicHash) {
         if (data == null) {
             new Exception("null SVG data").printStackTrace();
             return null;
@@ -340,7 +355,7 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         try {
             // NOTE: hard-coded to assume all SVG data is encoded in UTF-8
             byte[] dataBytes = data.getBytes("UTF-8");
-            String digest = digest(dataBytes, assignments);
+            String digest = digest(dataBytes, assignments, dynamicHash);
 
             SVGUniverse univ = SVGCache.getSVGUniverse();
             // TODO: this completely removes any parallel processing from the SVG loading which would be nice to have.
@@ -362,13 +377,12 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     }
 
     protected void initBuffer(Graphics2D g2d) {
-       
         if (!data.equals(documentCache) || diagramCache == null) {
-               dataHash = parseSVG();
-               if (diagramCache == null) {
-                       System.err.println("UNABLE TO PARSE SVG:\n" + data);
-                       return;
-               }
+            dataHash = parseSVG();
+            if (diagramCache == null) {
+                System.err.println("UNABLE TO PARSE SVG:\n" + data);
+                return;
+            }
         }
 
         if (buffer != null) {
@@ -380,14 +394,14 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
         } else if(diagramCache.getViewRect().getWidth()==0 || diagramCache.getViewRect().getHeight()==0) {
             buffer = null;
         } else if(useMipMap) {
-               if(G2DUtils.isAccelerated(g2d)) {
+            if(G2DUtils.isAccelerated(g2d)) {
                 buffer = new MipMapVRamBufferedImage(diagramCache, bounds, targetSize);
             } else {
                 buffer = new MipMapBufferedImage(diagramCache, bounds, targetSize);
             }
             bufferCache.put(dataHash, new WeakReference<BufferedImage>(buffer));
         } else {
-               if(G2DUtils.isAccelerated(g2d)) {
+            if(G2DUtils.isAccelerated(g2d)) {
                 buffer = new VRamBufferedImage(diagramCache, bounds, targetSize);
             } else {
                 buffer = new BufferedImage(diagramCache, bounds, targetSize);
@@ -419,13 +433,13 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
     }
 
     static WeakHashMap<String, String> digestCache = new WeakHashMap<String, String>();
-    
-    static String digest(byte[] dataBytes, List<SVGNodeAssignment> assignments) {
+
+    static String digest(byte[] dataBytes, List<SVGNodeAssignment> assignments, int dynamicHash) {
         try {
             MessageDigest md = MessageDigest.getInstance("MD5");
             byte[] messageDigest = md.digest(dataBytes);
             BigInteger number = new BigInteger(1, messageDigest);
-            String dataHash = number.toString(16) + (assignments != null ? assignments.hashCode() : 0);
+            String dataHash = number.toString(16) + (assignments != null ? assignments.hashCode() : 0) + 31 * dynamicHash;
             String result = digestCache.get(dataHash);
             if(result == null) {
                result = dataHash;
@@ -462,12 +476,12 @@ public class SVGNode extends G2DNode implements InitValueSupport, LoaderNode {
        public void synchronizeTransform(double[] data) {
                this.setTransform(new AffineTransform(data));
        }
-       
+
        public String getSVGText() {
                String ret = data.replace("<svg", "<g").replaceAll("svg>", "g>");
                //return diagramCache.toString();
                //return data.replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"><svg xmlns=\"http://www.w3.org/2000/svg\" overflow=\"visible\" version=\"1.1\"", "<g").replaceAll("svg>", "/g>");
                return ret;
        }
-       
+
 }
index 61353557defe11fa5f99f05bad9dbd93aa4dc8a5..29ef9954eb37a604a1ca593439e9614a1e3d4510 100644 (file)
@@ -16,7 +16,9 @@ import java.awt.Composite;
 import java.awt.Graphics2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
+import java.util.Map;
 
+import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.g2d.G2DRenderingHints;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.events.EventTypes;
@@ -35,6 +37,7 @@ public class SingleElementNode extends TransformNode implements InitValueSupport
     protected Boolean hidden = Boolean.FALSE;
     private transient Object key;
     private transient String typeClass;
+    private transient Map<String, Object> parameters;
 
     public void setKey(Object key) {
         this.key = key;
@@ -52,6 +55,23 @@ public class SingleElementNode extends TransformNode implements InitValueSupport
         return typeClass;
     }
 
+    public void setParameters(Map<String,Object> parameters) {
+        this.parameters = parameters;
+    }
+
+    public <T> T getParameter(String key) {
+        if (parameters != null) {
+            @SuppressWarnings("unchecked")
+            T t = (T) parameters.get(key);
+            if(t != null) return t;
+        }
+        INode parent = NodeUtil.getNearestParentOfType(this, SingleElementNode.class);
+        if (parent instanceof SingleElementNode) {
+            return ((SingleElementNode)parent).getParameter(key);
+        }
+        return null;
+    }
+
     public void setTransferableProvider(TransferableProvider transferableProvider) {
         if (transferableProvider != this.transferableProvider) {
             if (this.transferableProvider != null)
index 3213cf9b5419e7d501fdba77afd93218babc0c9e..92385a1ed3f9be600ffb0ea33d0ff5e10cda913b 100644 (file)
@@ -1,13 +1,12 @@
 package org.simantics.scl.osgi.internal;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
@@ -126,15 +125,24 @@ public class BundleModuleSource extends EncodedTextualModuleSource implements Up
             }
         }
     }
-    
-    private Path getPath() throws IOException {
-        try {
-            return Paths.get(FileLocator.toFileURL(url).toURI());
-        } catch (URISyntaxException e) {
-            throw new IOException(e);
+
+    /*
+     * This code is a copy from org.simantics.utils.ui.BundleUtils
+     */
+    public static File resolveWritableBundleFile(URL url) throws IOException {
+        // This returns file, jar, http etc. - essentially resolves the bundle protocol
+        URL resolved = FileLocator.resolve(url);
+        if (resolved.getProtocol().equals("file")) {
+            return new File(resolved.getPath());
         }
+        return null;
     }
-    
+
+    private Path getPath() throws IOException {
+        File file = resolveWritableBundleFile(url);
+        return file != null ? file.toPath() : null;
+    }
+
     @Override
     public boolean isUpdateable() {
         try {
index f3e3383f67cd8d5da36c519962e5084c9afa3570..62fd287c18f51458c0b6b4b79c6264601e87b8f0 100644 (file)
@@ -90,7 +90,7 @@ public final class BundleUtils {
     public static URL find(String bundleId, String path) {
         return find(Platform.getBundle(bundleId), path);
     }
-       
+
        public static File findFile(String bundleId, String path) throws IOException {
                URL url = find(bundleId, path);
                if (url == null)
@@ -98,5 +98,17 @@ public final class BundleUtils {
                url = FileLocator.toFileURL(url);
                return new File(url.getPath());
        }
-    
+
+    /**
+     * @since 1.31.0
+     */
+    public static File resolveWritableBundleFile(URL url) throws IOException {
+        // This returns file, jar, http etc. - essentially resolves the bundle protocol
+        URL resolved = FileLocator.resolve(url);
+        if (resolved.getProtocol().equals("file")) {
+            return new File(resolved.getPath());
+        }
+        return null;
+    }
+
 }