X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.modeling%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2FSCLScenegraph.java;h=675c4e52581eb81995e4888246011f59b0e30b74;hb=2d2faa8fa89c174e70dc5bd63fb5d56acc1925c1;hp=d3fd1cca9b9314cf4933c78d331fe22f349d85be;hpb=f03e4a46a823abda03a6e20c120ad08170a3838e;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java index d3fd1cca9..675c4e525 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/SCLScenegraph.java @@ -31,6 +31,7 @@ import javax.xml.transform.stream.StreamResult; import org.apache.batik.dom.GenericDOMImplementation; import org.apache.batik.svggen.SVGGeneratorContext; import org.apache.batik.svggen.SVGGraphics2D; +import org.apache.batik.svggen.SVGIDGenerator; import org.simantics.Simantics; import org.simantics.datatypes.literal.GUID; import org.simantics.db.ReadGraph; @@ -46,11 +47,13 @@ import org.simantics.diagram.elements.DiagramNodeUtil; import org.simantics.diagram.elements.TextGridNode; import org.simantics.diagram.elements.TextNode; import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.DataElementMap; +import org.simantics.g2d.diagram.participant.ElementPainter.SelectionShapeNode; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.IElement; import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; @@ -68,6 +71,7 @@ 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.LinkNode; import org.simantics.scenegraph.g2d.nodes.NavigationNode; import org.simantics.scenegraph.g2d.nodes.SVGNode; import org.simantics.scenegraph.g2d.nodes.SelectionNode; @@ -84,6 +88,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class SCLScenegraph { @@ -396,6 +401,22 @@ public class SCLScenegraph { return true; } + + static class UniqueIDGenerator extends SVGIDGenerator{ + + String overallId; + public UniqueIDGenerator(String overallId) { + super(); + this.overallId = overallId; + } + + @Override + public String generateID(String prefix) { + return super.generateID(overallId+prefix); + } + + } + static class Generator extends SVGGraphics2D { int elemLevel = 0; @@ -409,9 +430,14 @@ public class SCLScenegraph { } public Generator(Document document) { + this(document,null); + } + public Generator(Document document, String id) { super(document); // prevent batik comment in each g-element getGeneratorContext().setComment(null); + if (id != null) + getGeneratorContext().setIDGenerator(new UniqueIDGenerator(id)); } @Override @@ -637,6 +663,8 @@ public class SCLScenegraph { } + + private static String renderSVG0(double width, double height, ICanvasContext ctx, Function1, Map> mappingFunction) { // Get a DOMImplementation. @@ -647,7 +675,13 @@ public class SCLScenegraph { Document document = domImpl.createDocument(svgNS, "svg", null); // Create an instance of the SVG Generator. - SVGGraphics2D svgGenerator = new Generator(document); + IDiagram d = ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM); + Resource r = d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE); + SVGGraphics2D svgGenerator; + if (r != null) + svgGenerator = new Generator(document,r.toString()); + else + svgGenerator = new Generator(document); RenderSVGContext result = new RenderSVGContext(); @@ -658,8 +692,7 @@ public class SCLScenegraph { if (selection != null) { // This prevents workbench selection from being left over. // Also prevents scene graph crap from being left on the screen. - IDiagram d = ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM); - selection.setSelection(0, d.getElements()); + selection.setSelection(0, d.getElements()); } G2DSceneGraph sg = ctx.getSceneGraph(); @@ -673,22 +706,28 @@ public class SCLScenegraph { // get the bounds of the content Rectangle2D content = rtreeBounds; - // To account for dynamic padding of selection rectangles (5 units + stroke width) - int offset = 6; - double scale = width < 0 || height < 0 ? 1.0 : Math.min((width - 2*offset) / content.getWidth(), (height - 2*offset) / content.getHeight()); - - AffineTransform tr = new AffineTransform(); - tr.translate(offset, offset); - tr.scale(scale, scale); - tr.translate(-content.getX(), -content.getY()); - tr.getMatrix(matrix); + if (content != null) { + // To account for dynamic padding of selection rectangles (5 units + stroke width) + int offset = 6; + + double scale = width < 0 || height < 0 ? 1.0 : Math.min((width - 2*offset) / content.getWidth(), (height - 2*offset) / content.getHeight()); + + AffineTransform tr = new AffineTransform(); + tr.translate(offset, offset); + tr.scale(scale, scale); + tr.translate(-content.getX(), -content.getY()); + tr.getMatrix(matrix); + svgGenerator.setSVGCanvasSize(new Dimension((int)Math.ceil(scale * content.getWidth()) + 2*offset, (int)Math.ceil(scale * content.getHeight()) + 2*offset)); + } else { + svgGenerator.setSVGCanvasSize(new Dimension(100, 100)); + } //svgGenerator.translate(offset, offset); //svgGenerator.scale(scale, scale); // translate svgGenerator to the x and y coordinates of current content //svgGenerator.translate(-content.getX(), -content.getY()); - svgGenerator.setSVGCanvasSize(new Dimension((int)Math.ceil(scale * content.getWidth()) + 2*offset, (int)Math.ceil(scale * content.getHeight()) + 2*offset)); + //svgGenerator.setClip(content); result.append(MAIN_SECTION, ""); @@ -714,7 +753,11 @@ public class SCLScenegraph { result.append(ALL_SECTIONS, ""); StringBuilder res = new StringBuilder(); - res.append(""); + if (width > 0 && height > 0 ) { + res.append(""); + } else { + res.append(""); + } res.append(""); res.append(result.get(MAIN_SECTION)); res.append(result.get(SELECTION_SECTION)); @@ -775,15 +818,39 @@ public class SCLScenegraph { private String getKey(SingleElementNode node) { String key; if(node.getKey() != null) { - if (mappings.containsKey(node.getKey())) + if (mappings.containsKey(node.getKey())) { key = mappings.get(node.getKey()).toString(); - else + key = escape(key); + } else { key = node.getKey().toString(); + key = escape(key); + } } else { key = Long.toString(node.getId()); } return key; } + + private String escape(String key) { + // Keys may contain '<' '>' characters, which causes errors in browser SVG handling. + return org.apache.commons.lang.StringEscapeUtils.escapeHtml(key); + } + + private String removeElem(String xml, String elemStart) { + // FIXME: This is rather nasty and error prone way of removing elements from XML string. + // This only supports elements with /> end element tag. Elements ends with are not supported! + int start = xml.indexOf("<"+elemStart); + if (start>=0) { + int end = xml.indexOf(">",start); + if (end >= 0) { + if (start > 0) + return xml.substring(0,start)+xml.substring(end+1); + else + return xml.substring(end+1); + } + } + return null; + } @Override public void enter(IG2DNode node) { @@ -826,7 +893,8 @@ public class SCLScenegraph { parentBuilder.append(SELECTION_MASK_SECTION, "\n"); parentBuilder.append(SELECTION_SECTION, "\n"); - parentBuilder.append(MAIN_SECTION, "\n"); + senBuilders.put((ConnectionNode)node, new RenderSVGContext()); + } else if (isSelection0(node)) { @@ -878,7 +946,21 @@ public class SCLScenegraph { parentBuilder.append(MAIN_SECTION, "\n"); } } - parentBuilder.append(MAIN_SECTION, svg.getSVGText()); + String svgContent = svg.getSVGText(); + // SVGNode content may contain SVG/XML elements, which break browser compatibility + int start = svgContent.indexOf("= 0) + svgContent = svgContent.substring(start); + else { + String s = removeElem(svgContent, "?xml"); + if (s != null) { + svgContent = ""+s+""; + s = removeElem(svgContent, "!DOCTYPE"); + if (s != null) + svgContent = s; + } + } + parentBuilder.append(MAIN_SECTION, svgContent); if (!at.isIdentity()) { parentBuilder.append(MAIN_SECTION, "\n"); } @@ -906,6 +988,84 @@ public class SCLScenegraph { parentBuilder.append(ALL_SECTIONS, "\n"); } } + } else if (node instanceof TextNode) { + TextNode text = (TextNode)node; + + SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN != null) { + + text.setShowSelection(false); + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, svg); + + RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN); + + String key = getKey(parentSEN); + text.setShowSelection(true); + doc = renderSVGNode(svgGenerator, (IG2DNode)node); + svg = printSVGDocument(doc); + + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_SECTION, svg); + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_MASK_SECTION, "\n"); + Rectangle2D rect = text.getBounds(); + // NaN + if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { + parentBuilder2.append(SELECTION_MASK_SECTION,""); + } + parentBuilder2.append(SELECTION_MASK_SECTION,"\n"); + + } + } else if (!(node instanceof RouteGraphNode) && !(node instanceof LinkNode)){ + try { + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + NodeList gList = doc.getElementsByTagName("g"); + if (gList.getLength() == 0) + return; + boolean hasContent = false; + for (int i = 0; i < gList.getLength(); i++) { + Node gNode = gList.item(i); + if (gNode.hasChildNodes()) { + hasContent = true; + break; + } + } + if (!hasContent) + return; + String svg = printSVGDocument(doc); + if (node instanceof SelectionShapeNode) { + SingleElementNode parentSEN = (SingleElementNode)NodeUtil.getNearestParentOfType(node, SingleElementNode.class); + if(parentSEN != null) { + String key = getKey(parentSEN); + RenderSVGContext parentBuilder2 = getParentBuilder(parentSEN); + parentBuilder2.append(SELECTION_SECTION, "\n"); + parentBuilder2.append(SELECTION_SECTION, svg); + parentBuilder2.append(SELECTION_SECTION, "\n"); + + parentBuilder2.append(SELECTION_MASK_SECTION, "\n"); + Rectangle2D rect = node.getBounds(); + // NaN + if(rect.getHeight() == rect.getHeight() && rect.getWidth() == rect.getWidth()) { + parentBuilder2.append(SELECTION_MASK_SECTION,""); + } + parentBuilder2.append(SELECTION_MASK_SECTION,"\n"); + } + } else { + parentBuilder.append(MAIN_SECTION, ""); + parentBuilder.append(MAIN_SECTION, svg); + parentBuilder.append(MAIN_SECTION, "\n"); + } + } catch (Exception e) { + // TODO: There are nodes that do not behave well when rendered to SVG. For backwards compatibility, we don't handle the exceptions. + } } //enters.put(node, b.length()); @@ -938,8 +1098,28 @@ public class SCLScenegraph { @Override public void leave(IG2DNode node) { - if(node instanceof ConnectionNode || node instanceof SVGNode) { + if( node instanceof SVGNode) { // We are done + } else if (node instanceof ConnectionNode) { + RenderSVGContext parentBuilder = getParentBuilder(node); + SingleElementNode sen = (SingleElementNode)node; + RenderSVGContext b = senBuilders.get(sen); + String content = b.get(MAIN_SECTION); + if(content.isEmpty()) { + if(sen.getKey() != null) { + + for(SelectionNode n : NodeUtil.collectNodes(node, SelectionNode.class)) { + n.setIgnore(true); + } + + Element doc = renderSVGNode(svgGenerator, (IG2DNode)node); + String svg = printSVGDocument(doc); + parentBuilder.append(MAIN_SECTION, svg); + } + } else { + parentBuilder.append(b); + } + parentBuilder.append(MAIN_SECTION, "\n"); } else if (node instanceof G2DParentNode) { RenderSVGContext parentBuilder = getParentBuilder(node);