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
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
<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/>
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;
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;
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;
TextImpl.INSTANCE,
new StaticObjectAdapter(elementType),
DefaultTransform.INSTANCE,
+ DefaultParameters.INSTANCE,
StaticSymbolImageInitializer.INSTANCE,
new StaticSymbolImpl(img),
DefinedElementHandler.INSTANCE,
// 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);
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 {
n.setTransform(at); // FIXME: not tested..
}
}
+ if(parent instanceof SingleElementNode) {
+ ((SingleElementNode)parent).setParameters(ElementUtils.getParameters(e));
+ }
}
public void cleanup(final IElement e) {
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;
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);
}
--- /dev/null
+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
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;
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;
* 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;
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;
* 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;
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;
import com.kitfox.svg.SVGUniverse;
import com.kitfox.svg.xml.StyleAttribute;
-public class AnimatedSVGNode extends SVGNode {
+public class AnimatedSVGNode extends SVGNode implements AnimatedNode {
/**
*
*/
-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 {
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
* 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;
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;
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+
+}
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;
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;
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;
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;
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 {
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;
*/
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.
*/
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;
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 {
--- /dev/null
+/*******************************************************************************
+ * 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);
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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 {
+}
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;
}
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;
}
import java.awt.image.BufferedImage;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;
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");
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();
}
}
<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>
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;
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;
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;
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;
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;
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;
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;
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);
t2 = (float)v/1000;
}});
+ /*
svg1.setScript(script1);
svg2.setScript(script1);
e.printStackTrace();
}
}
-
+ */
+
close();
}
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,
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;
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();
g2d.setTransform(ot);
}
+ protected int dynamicHash() {
+ return 0;
+ }
+
protected String parseSVG() {
if (data == null)
return null;
diagramCache = null;
}
-
// Lets check for rootAssignment that contributes the whole SVG
SVGNodeAssignment rootAssignment = null;
if (!assignments.isEmpty()) {
// 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);
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());
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)) {
((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;
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.
}
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;
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.
}
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) {
} 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);
}
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;
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;
}
-
+
}
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;
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;
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)
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;
}
}
}
-
- 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 {
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)
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;
+ }
+
}