From: Tuukka Lehtonen Date: Fri, 1 Nov 2019 07:22:28 +0000 (+0000) Subject: Merge "Revert "Avoid duplicate evaluation of procedural component type requests"" X-Git-Tag: v1.43.0~136^2~38 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=a96e5125d72579c43abd70eb5c23de835324eaad;hp=85f0ea63f6e27f1274e8d02b8d2b8d50daa89597 Merge "Revert "Avoid duplicate evaluation of procedural component type requests"" --- diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/migration/MigrationStateImpl.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/migration/MigrationStateImpl.java index f50bae78a..fdd6f9132 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/migration/MigrationStateImpl.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/migration/MigrationStateImpl.java @@ -109,7 +109,7 @@ public class MigrationStateImpl implements MigrationState { try { File modelFile = getProperty(MigrationStateKeys.MODEL_FILE); TimeLogger.log(MigrationStateImpl.class, "reading TG into memory from " + modelFile); - TransferableGraph1 tg = TransferableGraphFileReader.read(modelFile); + TransferableGraph1 tg = TransferableGraphFileReader.read(modelFile, false); TimeLogger.log(MigrationStateImpl.class, "read TG into memory from " + modelFile); setProperty(MigrationStateKeys.CURRENT_TG, tg); return (T)tg; diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java index 3fcdf66ab..8d364c737 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java @@ -147,6 +147,13 @@ implements Read> { protected boolean parseAsBlock() { return false; } + + /* + * Override this to provide location information in compilation error situations. + */ + protected String getContextDescription(ReadGraph graph) throws DatabaseException { + return toString(); + } @SuppressWarnings("unchecked") private Function1 eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException { @@ -160,18 +167,20 @@ implements Read> { else throw e; } catch (SCLExpressionCompilationException e) { - LOGGER.error("Could not compile {}", evaluator.getExpressionText(), e); StringBuilder b = new StringBuilder(); b.append("Couldn't compile '"); b.append(evaluator.getExpressionText()); + b.append("' in "); + b.append(getContextDescription(graph)); b.append("':\n"); StringBuilder b2 = new StringBuilder(); for(CompilationError error : e.getErrors()) { b2.append(error.description); b2.append('\n'); } - System.err.println(b.toString() + b2.toString()); - throw new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors()); + SCLDatabaseException exception = new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors()); + LOGGER.info(exception.getMessage(), exception); + throw exception; } catch(Throwable e) { // Should not happen! LOGGER.error("This error should never happen", e); diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java index c32b0ee24..8a73ead7a 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java @@ -97,11 +97,11 @@ public class RouteLine implements RouteNode, Serializable { public boolean isNear(double x2, double y2, double tolerance) { return isHorizontal ? Math.abs(y2-position) <= tolerance - && points.get(0).x <= x2 - && x2 <= points.get(points.size()-1).x + && points.get(0).x <= x2 - tolerance + && x2 <= points.get(points.size()-1).x + tolerance : Math.abs(x2-position) <= tolerance - && points.get(0).y <= y2 - && y2 <= points.get(points.size()-1).y; + && points.get(0).y <= y2 - tolerance + && y2 <= points.get(points.size()-1).y + tolerance; } public void print(PrintStream out) { diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java index fd1603d39..a3d907482 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java @@ -25,7 +25,6 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.AsyncProcedure; import org.simantics.diagram.adapter.RouteGraphUtils.BackendConnection; import org.simantics.diagram.connection.RouteGraph; -import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.rendering.ConnectionStyle; import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer; import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; @@ -43,6 +42,7 @@ import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.TerminalTopology; import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; +import org.simantics.g2d.elementclass.RouteGraphConnectionClass; import org.simantics.g2d.utils.TopologicalSelectionExpander; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java index 17ccbe351..d6a59a0cb 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java @@ -35,7 +35,6 @@ import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.connection.ConnectionVisuals; import org.simantics.diagram.connection.RouteGraph; -import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteNode; import org.simantics.diagram.connection.RouteTerminal; @@ -66,6 +65,7 @@ import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; import org.simantics.g2d.element.handler.TerminalLayout; +import org.simantics.g2d.elementclass.RouteGraphConnectionClass; import org.simantics.g2d.elementclass.FlagClass.Type; import org.simantics.layer0.Layer0; import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent; diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectTool2.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectTool2.java index 38bdc706c..09adef2c8 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectTool2.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectTool2.java @@ -37,7 +37,6 @@ import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.connection.RouteGraph; -import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteTerminal; import org.simantics.diagram.connection.delta.RouteGraphDelta; @@ -79,6 +78,7 @@ import org.simantics.g2d.elementclass.BranchPoint; import org.simantics.g2d.elementclass.BranchPoint.Direction; import org.simantics.g2d.elementclass.FlagClass; import org.simantics.g2d.elementclass.FlagHandler; +import org.simantics.g2d.elementclass.RouteGraphConnectionClass; import org.simantics.g2d.participant.RenderingQualityInteractor; import org.simantics.g2d.participant.TransformUtil; import org.simantics.g2d.utils.geom.DirectionSet; diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java index e39c73d80..7454a2f39 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java @@ -16,6 +16,7 @@ import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Shape; import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayDeque; @@ -33,7 +34,6 @@ import org.simantics.db.WriteGraph; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.connection.RouteGraph; -import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RoutePoint; import org.simantics.diagram.connection.RouteTerminal; @@ -42,6 +42,7 @@ import org.simantics.diagram.connection.rendering.IRouteGraphRenderer; import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle; import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle; +import org.simantics.diagram.connection.segments.Segment; import org.simantics.diagram.synchronization.ISynchronizationContext; import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.diagram.synchronization.graph.RouteGraphConnection; @@ -77,6 +78,7 @@ import org.simantics.g2d.element.handler.impl.BranchPointTerminal; import org.simantics.g2d.element.impl.Element; import org.simantics.g2d.elementclass.FlagClass; import org.simantics.g2d.elementclass.FlagHandler; +import org.simantics.g2d.elementclass.RouteGraphConnectionClass; import org.simantics.g2d.participant.TransformUtil; import org.simantics.g2d.utils.geom.DirectionSet; import org.simantics.scenegraph.g2d.G2DParentNode; @@ -863,56 +865,53 @@ public class RouteGraphConnectTool extends AbstractMode { } private static RouteGraphTarget pickNearestRouteGraphConnection(ArrayList elements, double x, double y, double pd) { - // Find the nearest distance at which we get hits. - double hi = pd + 1; - double lo = hi * .01; - double limit = 0.5; - while (true) { - double delta = (hi - lo); - if (delta <= limit) - break; - - pd = (lo + hi) * .5; + Segment nearestSegment = null; + RouteLine nearestLine = null; + IElement nearestConnection = null; - boolean hit = false; - for (IElement connection : elements) { - RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE); - RouteLine line = rgn.getRouteGraph().pickLine(x, y, pd); - if (line != null) { - hit = true; - break; + double minDistanceSq = Double.MAX_VALUE; + + for (IElement connection : elements) { + RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE); + for (RouteLine line : rgn.getRouteGraph().getAllLines()) { + ArrayList segments = new ArrayList(); + line.collectSegments(segments); + for (Segment segment : segments) { + RoutePoint p1 = segment.p1; + RoutePoint p2 = segment.p2; + + double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); + if (distanceSq < minDistanceSq && distanceSq < Math.pow(pd + rgn.getSelectionStrokeWidth() / 2, 2)) { + minDistanceSq = distanceSq; + nearestSegment = segment; + nearestLine = line; + nearestConnection = connection; + } } } - - if (hit) - hi = pd; - else - lo = pd; } - // Now that the nearest hitting distance is found, find the nearest intersection. - RouteGraphTarget nearestTarget = null; - double nearest = Double.MAX_VALUE; - for (IElement connection : elements) { - RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE); - RouteLine line = rgn.getRouteGraph().pickLine(x, y, pd); - if (line == null) - continue; - - Point2D intersection = intersectionPoint(x, y, line); - if (intersection == null) - continue; - - double dx = intersection.getX() - x; - double dy = intersection.getY() - y; - double dist = Math.sqrt(dx*dx + dy*dy); - if (dist < nearest) { - nearest = dist; - nearestTarget = new RouteGraphTarget(connection, rgn, line, new Point2D.Double(x, y), intersection); + if (nearestSegment != null) { + RoutePoint p1 = nearestSegment.p1; + RoutePoint p2 = nearestSegment.p2; + double d = Math.pow(p2.getX() - p1.getX(), 2.0) + Math.pow(p2.getY() - p1.getY(), 2.0); + Point2D p; + if (d == 0) { + p = new Point2D.Double(p1.getX(), p1.getY()); + } else { + double u = ((x - p1.getX()) * (p2.getX() - p1.getX()) + (y - p1.getY()) * (p2.getY() - p1.getY())) / d; + if (u > 1.0) { + p = new Point2D.Double(p2.getX(), p2.getY()); + } else if (u <= 0.0) { + p = new Point2D.Double(p1.getX(), p1.getY()); + } else { + p = new Point2D.Double(p2.getX() * u + p1.getX() * (1.0-u), (p2.getY() * u + p1.getY() * (1.0- u))); + } } + return new RouteGraphTarget(nearestConnection, nearestConnection.getHint(RouteGraphConnectionClass.KEY_RG_NODE), nearestLine, new Point2D.Double(x, y), p); + } else { + return null; } - - return nearestTarget; } static Point2D intersectionPoint(double x, double y, RouteLine line) { diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java index dce4b34ab..adff8dab5 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java @@ -6,10 +6,12 @@ import java.util.Map; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.Statement; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.PossibleTypedParent; import org.simantics.db.common.request.UnaryRead; +import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.scl.AbstractExpressionCompilationContext; import org.simantics.db.layer0.scl.AbstractExpressionCompilationRequest; @@ -274,6 +276,20 @@ public class ServerSCLValueRequest extends AbstractExpressionCompilationRequest< public static Function1 validate(ReadGraph graph, Variable context) throws DatabaseException { return graph.syncRequest(new ServerSCLValueValidationRequest(graph, context), TransientCacheListener.instance()); } + + @Override + protected String getContextDescription(ReadGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Statement possibleOwner = graph.getPossibleStatement(literal, L0.IsOwnedBy); + if(possibleOwner != null) { + String uri = graph.getPossibleURI(possibleOwner.getObject()); + if(uri != null) { + String propertyName = NameUtils.getSafeName(graph, graph.getInverse(possibleOwner.getPredicate())); + return uri + "#" + propertyName; + } + } + return super.getContextDescription(graph); + } public static class ServerSCLValueValidationRequest extends ServerSCLValueRequest { diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java index 265caa805..912e79cd9 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java @@ -14,12 +14,17 @@ package org.simantics.g2d.diagram.handler; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; +import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import org.simantics.diagram.connection.RouteLine; +import org.simantics.diagram.connection.RoutePoint; +import org.simantics.diagram.connection.segments.Segment; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.connection.handler.ConnectionHandler; import org.simantics.g2d.element.IElement; @@ -28,8 +33,11 @@ import org.simantics.g2d.element.handler.TerminalTopology; import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline; import org.simantics.g2d.elementclass.BranchPoint; import org.simantics.g2d.elementclass.MonitorHandler; +import org.simantics.g2d.elementclass.RouteGraphConnectionClass; import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode; import org.simantics.scenegraph.utils.TransformedRectangle; +import org.simantics.utils.datastructures.Pair; /** * @@ -172,6 +180,64 @@ public class PickRequest { }); } }; + + /* + * First use the default sorting if available, then sort successive connections by their distance to cursor. + */ + public static PickSorter connectionSorter(PickSorter sorter, double x, double y) { + return new PickSorter() { + + private double getDistanceSqToRouteGraphConnection(RouteGraphNode rgn, double x, double y) { + double minDistanceSq = Double.MAX_VALUE; + for (RouteLine line : rgn.getRouteGraph().getAllLines()) { + ArrayList segments = new ArrayList(); + line.collectSegments(segments); + for (Segment segment : segments) { + RoutePoint p1 = segment.p1; + RoutePoint p2 = segment.p2; + + double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); + if (distanceSq < minDistanceSq) { + minDistanceSq = distanceSq; + } + } + } + return minDistanceSq; + } + + @Override + public void sort(List elements) { + if (sorter != null) + sorter.sort(elements); + + List> connections = new ArrayList<>(elements.size()); + int tail = 0; + for (int i = 0; i < elements.size(); i++) { + IElement element = elements.get(i); + RouteGraphNode rgn = element.getHint(RouteGraphConnectionClass.KEY_RG_NODE); + if (rgn != null) { + double distanceSq = getDistanceSqToRouteGraphConnection(rgn, x, y); + connections.add(new Pair(distanceSq, element)); + } + + if (rgn == null || i == elements.size() - 1) { + Collections.sort(connections, new Comparator>() { + @Override + public int compare(Pair connection1, Pair connection2) { + return Double.compare(connection2.first, connection1.first); + } + }); + for (Pair connection : connections) { + elements.set(tail, connection.second); + tail++; + } + connections.clear(); + tail = i + 1; + } + } + } + }; + } } } diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/PointerInteractor.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/PointerInteractor.java index 4f9df238f..940579ebe 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/PointerInteractor.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/PointerInteractor.java @@ -45,12 +45,14 @@ import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalIn import org.simantics.g2d.element.ElementClassProviders; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.IElementClassProvider; +import org.simantics.g2d.elementclass.RouteGraphConnectionClass; import org.simantics.g2d.participant.KeyUtil; import org.simantics.g2d.participant.MouseUtil; import org.simantics.g2d.participant.TransformUtil; import org.simantics.g2d.scenegraph.SceneGraphConstants; import org.simantics.g2d.utils.CanvasUtils; import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.scenegraph.g2d.G2DSceneGraph; import org.simantics.scenegraph.g2d.events.Event; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.scenegraph.g2d.events.KeyEvent; @@ -60,12 +62,15 @@ import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; import org.simantics.scenegraph.g2d.events.command.Commands; +import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode; import org.simantics.scenegraph.g2d.snap.ISnapAdvisor; import org.simantics.utils.ObjectUtils; import org.simantics.utils.datastructures.context.IContext; import org.simantics.utils.datastructures.context.IContextListener; +import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; +import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.threads.ThreadUtils; /** @@ -203,6 +208,14 @@ public class PointerInteractor extends AbstractDiagramParticipant { super.addedToContext(ctx); hoverStrategy = new DefaultHoverStrategy((TerminalHoverStrategy) getHint(TerminalPainter.TERMINAL_HOVER_STRATEGY)); setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, hoverStrategy); + + getContext().getSceneGraph().setGlobalProperty(G2DSceneGraph.PICK_DISTANCE, getPickDistance()); + getHintStack().addKeyHintListener(KEY_PICK_DISTANCE, new HintListenerAdapter() { + @Override + public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { + getContext().getSceneGraph().setGlobalProperty(G2DSceneGraph.PICK_DISTANCE, getPickDistance()); + } + }); } @EventHandler(priority = 0) @@ -297,7 +310,7 @@ public class PointerInteractor extends AbstractDiagramParticipant { return null; double pd = getPickDistance(); - Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2+1, pd*2+1); + Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2, pd*2); Shape canvasShape = GeometryUtils.transformShape(controlPickRect, inverse); return canvasShape; } @@ -415,15 +428,13 @@ public class PointerInteractor extends AbstractDiagramParticipant { assertDependencies(); - // Pick Terminal - double pickDist = getPickDistance(); - Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1); - Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform()); + Shape canvasPickRect = getCanvasPickShape(me.controlPosition); int selectionId = me.mouseId; PickRequest req = new PickRequest(canvasPickRect).context(getContext()); req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS; - req.pickSorter = pickSorter; + req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY()); + //req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST; List pickables = new ArrayList(); pickContext.pick(diagram, req, pickables); @@ -586,14 +597,13 @@ public class PointerInteractor extends AbstractDiagramParticipant { if (getToolMode() != Hints.POINTERTOOL) return false; if (me.clickCount < 2) return false; - // Pick Terminal - double pickDist = getPickDistance(); - Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1); - Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform()); + Shape canvasPickRect = getCanvasPickShape(me.controlPosition); int selectionId = me.mouseId; PickRequest req = new PickRequest(canvasPickRect).context(getContext()); req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS; + + req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY()); List pick = new ArrayList(); pickContext.pick(diagram, req, pick); @@ -641,23 +651,13 @@ public class PointerInteractor extends AbstractDiagramParticipant { assertDependencies(); Point2D curCanvasPos = util.controlToCanvas(me.controlPosition, curCanvasDragPos); - PickRequest req = new PickRequest(me.startCanvasPos).context(getContext()); + Shape canvasPickRect = getCanvasPickShape(me.controlPosition); + PickRequest req = new PickRequest(canvasPickRect).context(getContext()); req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS; + req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY()); List picks = new ArrayList(); pickContext.pick(diagram, req, picks); - //System.out.println(picks); - if (picks.isEmpty()) { - // Widen the area of searching if nothing is found with point picking - double pickDist = getPickDistance(); - Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1); - Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform()); - req = new PickRequest(canvasPickRect).context(getContext()); - req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS; - pickContext.pick(diagram, req, picks); - //System.out.println("2nd try: " + picks); - } - Set sel = selection.getSelection(me.mouseId); IElement topMostPick = picks.isEmpty() ? null : picks.get(picks.size() - 1); Set elementsToDrag = new HashSet(); @@ -686,6 +686,15 @@ public class PointerInteractor extends AbstractDiagramParticipant { getContext().add(tm); return !onlyConnections; } + } else { + // forward MouseDragBegin to closest RouteGraphNode + for (int i = picks.size() - 1; i >= 0; i--) { + RouteGraphNode rgn = picks.get(i).getHint(RouteGraphConnectionClass.KEY_RG_NODE); + if (rgn != null) { + rgn.handleDrag(me); + break; + } + } } } } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/connection/RouteGraphConnectionClass.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/RouteGraphConnectionClass.java similarity index 96% rename from bundles/org.simantics.diagram/src/org/simantics/diagram/connection/RouteGraphConnectionClass.java rename to bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/RouteGraphConnectionClass.java index 72b8232c1..bf4577b51 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/connection/RouteGraphConnectionClass.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/RouteGraphConnectionClass.java @@ -9,13 +9,14 @@ * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ -package org.simantics.diagram.connection; +package org.simantics.g2d.elementclass; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.util.Collection; import java.util.Collections; +import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.rendering.IRouteGraphRenderer; import org.simantics.g2d.connection.ConnectionEntity; import org.simantics.g2d.connection.handler.ConnectionHandler; @@ -170,6 +171,10 @@ public class RouteGraphConnectionClass { @Override public boolean pickTest(IElement e, Shape s, PickPolicy policy) { + RouteGraphNode rgn = e.getHint(KEY_RG_NODE); + if (rgn == null) { + return false; + } RouteGraph rg = getRouteGraph(e); if (rg == null) return false; @@ -184,7 +189,7 @@ public class RouteGraphConnectionClass { if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION)) tolerance = getTolerance(e); else - tolerance = (bounds.getHeight()+bounds.getHeight()) * 0.25; + tolerance = Math.max((bounds.getHeight()+bounds.getHeight()) * 0.25, rgn.getSelectionStrokeWidth() / 2); Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance); return node != null; } diff --git a/bundles/org.simantics.graph/src/org/simantics/graph/representation/TransferableGraphFileReader.java b/bundles/org.simantics.graph/src/org/simantics/graph/representation/TransferableGraphFileReader.java index 6fc7ac551..c7cc41504 100644 --- a/bundles/org.simantics.graph/src/org/simantics/graph/representation/TransferableGraphFileReader.java +++ b/bundles/org.simantics.graph/src/org/simantics/graph/representation/TransferableGraphFileReader.java @@ -5,13 +5,13 @@ import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.List; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; +import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.RuntimeDatatypeConstructionException; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.container.DataContainer; @@ -26,9 +26,45 @@ import org.slf4j.LoggerFactory; /** * It is recommended to use {@link #read(File)} and {@link #read(InputStream)} * for reading to ensure proper resource handling. + * + *

+ * It is important to use the correct setting for + * {@link #sharedValueContext(boolean)} which depends on how the TG was + * serialized to begin with. See {@link #DEFAULT_SHARED_VALUE_CONTEXT} for more + * information on this. */ final public class TransferableGraphFileReader extends ByteFileReader { + /** + * Serializing TransferableGraph1 structures using the default {@link Binding} + * will use shared context for serializing the values Variant array. Thus all TG + * files produced by the graph compiler use a shared value context which means + * this class must be used with {@link #sharedValueContext(boolean)} set to + * true. As an example, true must be used if the corresponding TG + * file is written e.g. like this: + *

+     * DataContainers.writeFile(location 
+     *     new DataContainer(format,
+     *                       version,
+     *                       metadata,
+     *                       new Variant(TransferableGraph1.BINDING, tg)));
+     * 
+ * + *

+ * On the other hand, any TG files serialized using more optimized methods like + * ModelTransferableGraphSource do not use a shared value context + * when writing the file. This means those files cannot be read safely using + * standard {@link Binding} at all, and when using this class, + * {@link #sharedValueContext(boolean)} must be set to false to prevent the + * import from corrupting datatype values because the referable parts of + * datatypes may get bound to the wrong existing types if the data is read using + * shared context. + * + *

+ * true is the default setting. + */ + public static final boolean DEFAULT_SHARED_VALUE_CONTEXT = true; + private static final Logger LOGGER = LoggerFactory.getLogger(TransferableGraphFileReader.class); InputStream in = new InputStream() { @@ -59,6 +95,7 @@ final public class TransferableGraphFileReader extends ByteFileReader { final private static int SIZE = 1<<18; final private static int HEADER = headerSize(); final private int header; + private boolean shareValueContext = true; /** * Reads a {@link DataContainer} containing a {@link TransferableGraph1} @@ -69,8 +106,34 @@ final public class TransferableGraphFileReader extends ByteFileReader { * @throws IOException */ public static TransferableGraph1 read(File file) throws IOException { + return read(file, DEFAULT_SHARED_VALUE_CONTEXT); + } + + /** + * Reads a {@link DataContainer} containing a {@link TransferableGraph1} + * structure from the specified InputStream. Note that this implementation does + * not close the specified input stream, it is expected to be + * closed by the caller. + * + * @param input the input stream to read from + * @return the TG contained by the stream + * @throws IOException + */ + public static TransferableGraph1 read(InputStream input) throws IOException { + return read(input, DEFAULT_SHARED_VALUE_CONTEXT); + } + + /** + * Reads a {@link DataContainer} containing a {@link TransferableGraph1} + * structure from the specified {@link File}. + * + * @param file the file to read from + * @return the TG contained by the file + * @throws IOException + */ + public static TransferableGraph1 read(File file, boolean sharedValueContext) throws IOException { try (TransferableGraphFileReader reader = new TransferableGraphFileReader(file)) { - return reader.readTG(); + return reader.sharedValueContext(sharedValueContext).readTG(); } } @@ -84,9 +147,9 @@ final public class TransferableGraphFileReader extends ByteFileReader { * @return the TG contained by the stream * @throws IOException */ - public static TransferableGraph1 read(InputStream input) throws IOException { + public static TransferableGraph1 read(InputStream input, boolean sharedValueContext) throws IOException { try (TransferableGraphFileReader reader = new TransferableGraphFileReader(input)) { - return reader.readTG(); + return reader.sharedValueContext(sharedValueContext).readTG(); } } @@ -135,6 +198,11 @@ final public class TransferableGraphFileReader extends ByteFileReader { this.header = HEADER; } + public TransferableGraphFileReader sharedValueContext(boolean share) { + this.shareValueContext = share; + return this; + } + private static int headerSize() { try { return Bindings.getSerializerUnchecked(Datatype.class).serialize(Datatypes.getDatatypeUnchecked(TransferableGraph1.class)).length; @@ -146,7 +214,7 @@ final public class TransferableGraphFileReader extends ByteFileReader { throw new Error("Failed to determine TransferableGraph1 header size. ", e); } } - + public TransferableGraph1 readTG() throws IOException { if(getSize() == 0) return null; @@ -269,7 +337,8 @@ final public class TransferableGraphFileReader extends ByteFileReader { for(int i=0;i + + + + + + diff --git a/bundles/org.simantics.platform.ui/.project b/bundles/org.simantics.platform.ui/.project new file mode 100644 index 000000000..f85097827 --- /dev/null +++ b/bundles/org.simantics.platform.ui/.project @@ -0,0 +1,28 @@ + + + org.simantics.platform.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF b/bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..f0e776e41 --- /dev/null +++ b/bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Platform UI Components +Bundle-SymbolicName: org.simantics.platform.ui +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: Semantum Oy +Automatic-Module-Name: org.simantics.platform.ui +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.eclipse.ui.console;bundle-version="3.7.0", + org.eclipse.ui;bundle-version="3.109.0", + org.eclipse.core.jobs;bundle-version="3.9.0", + org.eclipse.jface.text;bundle-version="3.12.0", + org.eclipse.core.runtime;bundle-version="3.13.0", + org.simantics.ui, + org.slf4j.api, + ch.qos.logback.core, + ch.qos.logback.classic +Bundle-ActivationPolicy: lazy +Bundle-Activator: org.simantics.platform.ui.internal.Activator +Export-Package: org.simantics.platform.ui diff --git a/bundles/org.simantics.platform.ui/build.properties b/bundles/org.simantics.platform.ui/build.properties new file mode 100644 index 000000000..41eb6ade2 --- /dev/null +++ b/bundles/org.simantics.platform.ui/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java new file mode 100644 index 000000000..c7aa2e185 --- /dev/null +++ b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2019 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.platform.ui; + +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.MessageConsole; +import org.eclipse.ui.console.MessageConsoleStream; + +public class SimanticsConsole extends MessageConsole { + + // We don't want to constantly lose data, but this is still only half MB + private static int HIGH_WATERMARK = 2<<18; + // 75% + private static int LOW_WATERMARK = 3 * (HIGH_WATERMARK>>2); + + private ExecutorService executor = Executors.newSingleThreadExecutor(); + + private Color black; + private Color red; + private Color green; + private Color yellow; + private Color blue; + private Color magenta; + private Color cyan; + private Color white; + + private MessageConsoleStream stream; + + public SimanticsConsole() { + super("Simantics Console", "Simantics Console", null, true); + } + + @Override + protected void init() { + Display d = Display.getDefault(); + + black = tuneColor(d, SWT.COLOR_BLACK); + red = tuneColor(d, SWT.COLOR_RED); + green = tuneColor(d, SWT.COLOR_GREEN); + yellow = tuneColor(d, SWT.COLOR_YELLOW); + blue = tuneColor(d, SWT.COLOR_BLUE); + magenta = tuneColor(d, SWT.COLOR_MAGENTA); + cyan = tuneColor(d, SWT.COLOR_CYAN); + white = tuneColor(d, SWT.COLOR_WHITE); + + setBackground(black); + stream = newMessageStream(); + stream.setFontStyle(SWT.NORMAL); + stream.setColor(white); + + setWaterMarks(LOW_WATERMARK, HIGH_WATERMARK); + + } + + @Override + protected void dispose() { + super.dispose(); + executor.shutdown(); + try { + stream.close(); + } catch (IOException e) { + // nothing to do + } + + black.dispose(); + red.dispose(); + green.dispose(); + green.dispose(); + yellow.dispose(); + blue.dispose(); + magenta.dispose(); + cyan.dispose(); + white.dispose(); + } + + private Color tuneColor(Display d, int color) { + Color c = d.getSystemColor(color); + return new Color(d, (int)(c.getRed() * 0.75), (int)(c.getGreen() * 0.75), (int)(c.getBlue() * 0.75)); + } + + public static SimanticsConsole show() { + + if(!Thread.currentThread().equals(Display.getDefault().getThread())) { + Display.getDefault().asyncExec(() -> show()); + return null; + } + + for (IConsole c : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) { + if (c instanceof SimanticsConsole) { + SimanticsConsole sc = (SimanticsConsole) c; + sc.activate(); + return sc; + } + } + + SimanticsConsole sc = new SimanticsConsole(); + ConsolePlugin.getDefault().getConsoleManager().addConsoles(new SimanticsConsole[] {sc}); + return sc; + + } + + public static SimanticsConsole findConsole() { + + for (IConsole c : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) { + if (c instanceof SimanticsConsole) { + return (SimanticsConsole) c; + } + } + + SimanticsConsole sc = new SimanticsConsole(); + ConsolePlugin.getDefault().getConsoleManager().addConsoles(new SimanticsConsole[] {sc}); + return sc; + + } + + public void write(String data) { + executor.submit(() -> { + Pattern p = Pattern.compile("(\\x1b\\[(?.*?(m)))"); + Matcher m = p.matcher(data); + int pos = 0; + while (m.find()) { + if (m.start() > pos) { + stream.print(data.substring(pos, m.start())); + } + pos = m.end(); + + String cmd = m.group("cmd"); + if (cmd.endsWith("m")) { + // Stream style can be set only in UI thread + Display.getDefault().syncExec(() -> { + int fontStyle = stream.getFontStyle(); + Color color = stream.getColor(); + try { + stream.close(); + } catch (IOException e) { + // nothing to do + } + stream = newMessageStream(); + + String attributes[] = cmd.substring(0, cmd.length() - 1).split(";"); + for (String attribute : attributes) { + switch (Integer.parseInt(attribute)) { + case 0: fontStyle = SWT.NORMAL; break; + case 1: fontStyle = SWT.BOLD; break; + case 4: break; // underline + case 5: break; // blink + case 7: break; // reverse video + case 8: break; // nondisplayed + case 30: color = black; break; + case 31: color = red; break; + case 32: color = green; break; + case 33: color = yellow; break; + case 34: color = blue; break; + case 35: color = magenta; break; + case 36: color = cyan; break; + case 37: color = white; break; + // background colors not supported + default:break; + } + } + stream.setColor(color); + stream.setFontStyle(fontStyle); + }); + } + } + if (pos < data.length()) { + stream.print(data.substring(pos)); + } + stream.println(); + }); + } +} \ No newline at end of file diff --git a/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java new file mode 100644 index 000000000..50c4ff590 --- /dev/null +++ b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2019 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.platform.ui.internal; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.simantics.UnhandledExceptionService; +import org.simantics.platform.ui.SimanticsConsole; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Activator extends AbstractUIPlugin { + + public Activator() { + } + + @Override + public void start(BundleContext context) throws Exception { + + super.start(context); + + SimanticsConsole console = SimanticsConsole.findConsole(); + if(console != null) { + ServiceReference ref = context.getServiceReference(UnhandledExceptionService.class.getName()); + if (ref != null) { + UnhandledExceptionService service = (UnhandledExceptionService) context.getService(ref); + service.registerHandler(t -> { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + console.write(sw.toString()); + }); + } + ConsoleAppender ca = new ConsoleAppender(console); + ch.qos.logback.classic.Logger logbackLogger = + (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + logbackLogger.addAppender(ca); + ca.start(); + } + + } + +} diff --git a/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java new file mode 100644 index 000000000..8c3bf3705 --- /dev/null +++ b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2019 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.platform.ui.internal; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import org.simantics.platform.ui.SimanticsConsole; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +public class ConsoleAppender extends AppenderBase { + + final private SimanticsConsole console; + + final private DateFormat formatter; + + ConsoleAppender(SimanticsConsole console) { + assert(console != null); + this.console = console; + formatter = new SimpleDateFormat("dd.LL.yyyy HH:mm:ss.SSS"); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + @Override + protected void append(ILoggingEvent e) { + StringBuilder b = new StringBuilder(); + b.append('['); + b.append(formatter.format(new Date(e.getTimeStamp()))); + b.append("]: "); + b.append(e.getMessage()); + console.write(b.toString()); + } + +} \ No newline at end of file diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java index 9055a8efe..a013dc041 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java @@ -48,6 +48,7 @@ public class G2DSceneGraph extends G2DParentNode implements ILookupService, INod private static final long serialVersionUID = -7066146333849901429L; public static final String IGNORE_FOCUS = "ignoreFocus"; + public static final String PICK_DISTANCE = "pickDistance"; protected transient Container rootPane = null; // TODO: swing dependency in here might not be a good idea diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java index 0a2583fc6..048514f3a 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java @@ -23,7 +23,6 @@ import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.lang.reflect.Constructor; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -47,6 +46,7 @@ import org.simantics.scenegraph.INode; import org.simantics.scenegraph.ISelectionPainterNode; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.G2DSceneGraph; import org.simantics.scenegraph.g2d.IG2DNode; import org.simantics.scenegraph.g2d.events.EventTypes; import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent; @@ -61,6 +61,7 @@ import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.scenegraph.g2d.events.command.Commands; import org.simantics.scenegraph.g2d.nodes.GridNode; import org.simantics.scenegraph.g2d.nodes.LinkNode; +import org.simantics.scenegraph.g2d.nodes.NavigationNode; import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment; import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Action; import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Pick; @@ -69,8 +70,8 @@ import org.simantics.scenegraph.utils.ColorUtil; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.scenegraph.utils.InitValueSupport; import org.simantics.scenegraph.utils.NodeUtil; -import org.slf4j.LoggerFactory; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import gnu.trove.map.hash.THashMap; @@ -486,7 +487,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In return null; } - private double getSelectionStrokeWidth() { + public double getSelectionStrokeWidth() { if (selectionStroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) selectionStroke; return bs.getLineWidth(); @@ -530,6 +531,12 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In @Override protected boolean mouseDragged(MouseDragBegin e) { + // Consume event if drag is possible. + // PointerInteractor will call handleDrag with the MouseDragBegin event for the route line that is closest to the cursor. + return currentAction != null; + } + + public boolean handleDrag(MouseDragBegin e) { if (dragAction != null && !e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.button == MouseEvent.LEFT_BUTTON) { currentAction = dragAction; dragAction = null; @@ -549,7 +556,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In } } if (newBranchPointPosition != null) { - RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance); + RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance()); if (line != null) { newBranchPointPosition.setLocation(mouseX, mouseY); SplittedRouteGraph.snapToLine(newBranchPointPosition, line); @@ -660,7 +667,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In return false; } //System.out.println("move action"); - dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, pickTolerance, moveFilter, getSnapAdvisor()); + dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, scaledPickTolerance(), moveFilter, getSnapAdvisor()); //System.out.println("DRAG ACTION: " + dragAction); } @@ -671,6 +678,20 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In return false; } + private double scaledPickTolerance() { + NavigationNode nn = NodeUtil.findNearestParentNode(this, NavigationNode.class); + double scale = 1.0; + if (nn != null) { + scale = GeometryUtils.getScale(nn.getTransform()); + } + double pickDistance = 0; + G2DSceneGraph sg = NodeUtil.getRootNode(nn != null ? nn : this); + if (sg != null) { + pickDistance = sg.getGlobalProperty(G2DSceneGraph.PICK_DISTANCE, pickDistance); + } + return Math.max(getSelectionStrokeWidth() / 2.0, pickDistance / scale); + } + /** * Checks the selections data node in the scene graph for any links * @return @@ -755,11 +776,11 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In return false; if (!e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.keyCode == KeyEvent.VK_S) { - Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES); + Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES); return splitTarget(target); } else if (!e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK | MouseEvent.CTRL_MASK) && (e.keyCode == KeyEvent.VK_R || e.keyCode == KeyEvent.VK_D)) { - Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES); + Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES); return deleteTarget(target); } else if (e.keyCode == KeyEvent.VK_ESCAPE) { @@ -783,7 +804,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In } else if (e.keyCode == KeyEvent.VK_ALT) { // Begin connection branching visualization. - RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance); + RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance()); if (branchable && line != null) { newBranchPointPosition = new Point2D.Double(mouseX, mouseY); SplittedRouteGraph.snapToLine(newBranchPointPosition, line); diff --git a/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/utils/MappingBase.java b/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/utils/MappingBase.java index e50d8498d..7fd78189f 100644 --- a/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/utils/MappingBase.java +++ b/bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/utils/MappingBase.java @@ -1,14 +1,18 @@ package org.simantics.structural.synchronization.utils; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.Map; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectObjectProcedure; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; -import java.io.PrintWriter; -import java.util.Map; -import java.util.function.Consumer; - /** * The entry point to the mapping structure between Simantics database and a * designated solver. It is used to synchronize changes from Simantics to the @@ -18,6 +22,8 @@ import java.util.function.Consumer; */ abstract public class MappingBase> { + private final transient Logger LOGGER = LoggerFactory.getLogger(getClass()); + abstract public T getConfiguration(); /** @@ -98,8 +104,13 @@ abstract public class MappingBase> { public Map getConfigurationBySolverName() { Map result = configurationBySolverName; - if (result == null) - result = configurationBySolverName = createConfigurationBySolverName(getConfiguration()); + if (result == null) { + T configuration = getConfiguration(); + if (configuration != null) + result = configurationBySolverName = createConfigurationBySolverName(configuration); + else + result = Collections.emptyMap(); + } return result; } @@ -112,7 +123,11 @@ abstract public class MappingBase> { private void browseConfigurationBySolverName( THashMap configurationBySolverName, T configuration) { - configurationBySolverName.put(configuration.solverComponentName, configuration); + if (configuration.solverComponentName != null) { + configurationBySolverName.put(configuration.solverComponentName, configuration); + } else if (configuration.componentId != 0) { + LOGGER.warn("configuration.solverComponentName is null! configuration uid is {} and component id {}", configuration.getUid(), configuration.componentId); + } for(T child : configuration.getChildren()) { browseConfigurationBySolverName(configurationBySolverName, child); child.parent = configuration; @@ -124,6 +139,7 @@ abstract public class MappingBase> { if(result == null) { result = componentFactory.create(uid); configurationByUid.put(uid, result); + configurationBySolverName = null; // forces recalculation } else { if(result.getParent() == null) @@ -170,7 +186,7 @@ abstract public class MappingBase> { public void remove(final Solver solver, T component) { if(configurationByUid != null) configurationByUid.remove(component.uid); - if (configurationBySolverName != null) + if (configurationBySolverName != null && component.solverComponentName != null) configurationBySolverName.remove(component.solverComponentName); if(component.getChildMap() != null) component.getChildMap().forEachValue(new TObjectProcedure() { diff --git a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java index c4b4e341f..fce1abaa2 100644 --- a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java +++ b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java @@ -26,6 +26,8 @@ import org.simantics.scl.compiler.runtime.RuntimeEnvironment; import org.simantics.scl.compiler.top.SCLExpressionCompilationException; import org.simantics.scl.compiler.types.Type; import org.simantics.structural2.scl.AbstractCompileStructuralValueRequest.CompilationContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Compiles an SCL expression that is attached to a literal @@ -35,6 +37,8 @@ import org.simantics.structural2.scl.AbstractCompileStructuralValueRequest.Compi */ public abstract class AbstractCompileStructuralValueRequest extends AbstractExpressionCompilationRequest { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCompileStructuralValueRequest.class); + protected final Resource relation; public static class CompilationContext extends AbstractExpressionCompilationContext { @@ -53,8 +57,27 @@ public abstract class AbstractCompileStructuralValueRequest extends AbstractExpr @Override abstract protected String getExpressionText(ReadGraph graph) throws DatabaseException; + + /* + * @throws DatabaseException is an index root could not be found + */ abstract protected Resource getIndexRoot(ReadGraph graph) throws DatabaseException; + /* + * Does not throw if the graph is valid. + * + * @return null if component type was not availble + * + */ abstract protected Resource getComponentType(ReadGraph graph) throws DatabaseException; + + protected Resource getPossibleIndexRoot(ReadGraph graph) throws DatabaseException { + try { + return getIndexRoot(graph); + } catch (DatabaseException e) { + LOGGER.error("Error while resolving index root during structural value compilation", e); + return null; + } + } @Override protected Type getExpectedType(ReadGraph graph, CompilationContext context) @@ -135,4 +158,23 @@ public abstract class AbstractCompileStructuralValueRequest extends AbstractExpr return null; } + @Override + protected String getContextDescription(ReadGraph graph) throws DatabaseException { + Resource componentType = getComponentType(graph); + if(componentType != null) { + String uri = graph.getPossibleURI(componentType); + if(uri != null) + return uri; + } + // OK, no component type - report index root then + Resource indexRoot = getPossibleIndexRoot(graph); + if(indexRoot != null) { + String uri = graph.getPossibleURI(componentType); + if(uri != null) + return uri; + } + // OK, nothing - report default + return super.getContextDescription(graph); + } + } diff --git a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java index 5e564cc91..ce824090d 100644 --- a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java +++ b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java @@ -118,4 +118,16 @@ public class CompileStructuralValueRequest extends AbstractCompileStructuralValu return true; } + @Override + protected String getContextDescription(ReadGraph graph) throws DatabaseException { + if(component != null) { + String uri = graph.getPossibleURI(component); + if(uri != null) { + String propertyName = NameUtils.getSafeName(graph, relation); + return uri + "#" + propertyName; + } + } + return super.getContextDescription(graph); + } + } diff --git a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java index db6cdac80..95ab99299 100644 --- a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java +++ b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; +import org.simantics.internal.UnhandledExceptionServiceImpl; import org.simantics.ui.workbench.WorkbenchShutdownServiceImpl; /** @@ -39,6 +40,7 @@ public class Activator extends AbstractUIPlugin { public void start(BundleContext context) throws Exception { WorkbenchShutdownServiceImpl.initialize(context); + UnhandledExceptionServiceImpl.initialize(context); super.start(context); @@ -51,6 +53,7 @@ public class Activator extends AbstractUIPlugin { public void stop(BundleContext context) throws Exception { WorkbenchShutdownServiceImpl.close(); + UnhandledExceptionServiceImpl.close(); super.stop(context); diff --git a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java index cb1ee16d3..f5bda18b0 100644 --- a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java +++ b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java @@ -82,7 +82,9 @@ import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog; import org.eclipse.ui.keys.IBindingService; import org.eclipse.ui.progress.IProgressService; import org.eclipse.ui.statushandlers.AbstractStatusHandler; +import org.eclipse.ui.statushandlers.StatusAdapter; import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; import org.simantics.CancelStartupException; @@ -92,6 +94,7 @@ import org.simantics.SimanticsPlatform; import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy; import org.simantics.SimanticsPlatform.RecoveryPolicy; import org.simantics.TimingProgressMonitor; +import org.simantics.UnhandledExceptionService; import org.simantics.application.arguments.IArguments; import org.simantics.application.arguments.SimanticsArguments; import org.simantics.db.common.Indexing; @@ -102,6 +105,7 @@ import org.simantics.ui.SimanticsUI; import org.simantics.ui.jobs.SessionGarbageCollectorJob; import org.simantics.ui.workbench.PerspectiveBarsActivator; import org.simantics.ui.workbench.PerspectiveContextActivator; +import org.simantics.ui.workbench.WorkbenchShutdownService; import org.simantics.utils.logging.TimeLogger; import org.simantics.utils.ui.dialogs.ShowError; import org.simantics.utils.ui.dialogs.ShowMessage; @@ -1133,17 +1137,17 @@ public class SimanticsWorkbenchAdvisor extends WorkbenchAdvisor { * location is not being shown */ public String getWorkspaceLocation() { - // read command line, which has priority - IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class); - String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null; - if (location != null) { - return location; - } - // read the preference - if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) { - return Platform.getLocation().toOSString(); - } - return null; + // read command line, which has priority + IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class); + String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null; + if (location != null) { + return location; + } + // read the preference + if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) { + return Platform.getLocation().toOSString(); + } + return null; } /** @@ -1175,13 +1179,30 @@ public class SimanticsWorkbenchAdvisor extends WorkbenchAdvisor { * * @see org.eclipse.ui.application.WorkbenchAdvisor#getWorkbenchErrorHandler() */ + + private AbstractStatusHandler workbenchErrorHandler = new AbstractStatusHandler() { + + @Override + public void handle(StatusAdapter statusAdapter, int style) { + if (ideWorkbenchErrorHandler == null) { + ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler( + getWorkbenchConfigurer()); + } + ideWorkbenchErrorHandler.handle(statusAdapter, style); + + BundleContext context = Activator.getDefault().getBundle().getBundleContext(); + ServiceReference ref = context.getServiceReference(UnhandledExceptionService.class.getName()); + UnhandledExceptionService unhandled = (UnhandledExceptionService)context.getService(ref); + Throwable t = statusAdapter.getStatus().getException(); + if(t != null) + unhandled.handle(t); + + } + }; + @Override public AbstractStatusHandler getWorkbenchErrorHandler() { - if (ideWorkbenchErrorHandler == null) { - ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler( - getWorkbenchConfigurer()); - } - return ideWorkbenchErrorHandler; + return workbenchErrorHandler; } /* (non-Javadoc) diff --git a/bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java b/bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java new file mode 100644 index 000000000..d09485161 --- /dev/null +++ b/bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2019 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; + +import java.util.function.Consumer; + +/** + * @author Antti Villberg + */ +public interface UnhandledExceptionService { + + void registerHandler(Consumer handler); + void handle(Throwable t); + +} diff --git a/bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java b/bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java new file mode 100644 index 000000000..4276dc9e8 --- /dev/null +++ b/bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2019 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.internal; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.function.Consumer; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.simantics.UnhandledExceptionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Antti Villberg + */ +public class UnhandledExceptionServiceImpl implements UnhandledExceptionService { + + private static final Logger LOGGER = LoggerFactory.getLogger(UnhandledExceptionServiceImpl.class); + private static ServiceRegistration service = null; + + private final List> handlers = new ArrayList<>(); + + /** + * Invoked by the bundle activator to initialize the cache service. + * + * @param context the bundle context to register the service with + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public synchronized static void initialize(BundleContext context) { + if (service == null) { + service = context.registerService(UnhandledExceptionService.class.getName(), new UnhandledExceptionServiceImpl(), new Hashtable()); + } + } + + /** + * Invoked by the bundle activator to close the cache service. + */ + public synchronized static void close() { + if (service != null) { + service.unregister(); + service = null; + } + } + + @Override + public synchronized void registerHandler(Consumer handler) { + handlers.add(handler); + } + + @Override + public synchronized void handle(Throwable t) { + for (Consumer handler : handlers) { + try { + handler.accept(t); + } catch (Exception e) { + handleException(handler, e); + } catch (LinkageError e) { + handleException(handler, e); + } catch (AssertionError e) { + handleException(handler, e); + } + } + } + + protected void handleException(Object source, Throwable t) { + LOGGER.error("{}: Unhandled exception handler {} caused unexpected exception", getClass().getSimpleName(), source, t); + } + +} diff --git a/bundles/pom.xml b/bundles/pom.xml index e90980faf..d8cdbb346 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -177,6 +177,7 @@ org.simantics.msvc.runtime.x86_64 org.simantics.nativemem org.simantics.objmap2 + org.simantics.platform.ui org.simantics.platform.ui.ontology org.simantics.project org.simantics.project.ontology diff --git a/features/org.simantics.ui.workbench.feature/feature.xml b/features/org.simantics.ui.workbench.feature/feature.xml index 06ab0f00e..72baed8b3 100644 --- a/features/org.simantics.ui.workbench.feature/feature.xml +++ b/features/org.simantics.ui.workbench.feature/feature.xml @@ -277,4 +277,11 @@ version="0.0.0" unpack="false"/> + +