]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Merge "Revert "Avoid duplicate evaluation of procedural component type requests""
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Fri, 1 Nov 2019 07:22:28 +0000 (07:22 +0000)
committerGerrit Code Review <gerrit2@simantics>
Fri, 1 Nov 2019 07:22:28 +0000 (07:22 +0000)
33 files changed:
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/migration/MigrationStateImpl.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java
bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphConnectionClassFactory.java
bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java
bundles/org.simantics.diagram/src/org/simantics/diagram/participant/ConnectTool2.java
bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java
bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/PointerInteractor.java
bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/RouteGraphConnectionClass.java [moved from bundles/org.simantics.diagram/src/org/simantics/diagram/connection/RouteGraphConnectionClass.java with 96% similarity]
bundles/org.simantics.graph/src/org/simantics/graph/representation/TransferableGraphFileReader.java
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/ConnectionPointNameStyle.java
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/TypicalInheritanceStyle.java
bundles/org.simantics.modeling/src/org/simantics/modeling/typicals/TypicalUtil.java
bundles/org.simantics.platform.ui/.classpath [new file with mode: 0644]
bundles/org.simantics.platform.ui/.project [new file with mode: 0644]
bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF [new file with mode: 0644]
bundles/org.simantics.platform.ui/build.properties [new file with mode: 0644]
bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java [new file with mode: 0644]
bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java [new file with mode: 0644]
bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java [new file with mode: 0644]
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java
bundles/org.simantics.structural.synchronization/src/org/simantics/structural/synchronization/utils/MappingBase.java
bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java
bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java
bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java
bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java
bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java [new file with mode: 0644]
bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java [new file with mode: 0644]
bundles/pom.xml
features/org.simantics.ui.workbench.feature/feature.xml

index f50bae78aa2e8c8ebf702efd86c472c436ce2bea..fdd6f9132fe4b78d45d6874275778ccdc4481465 100644 (file)
@@ -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;
index 3fcdf66ab042679ff7f0fde8e0c1cf520b8d8037..8d364c737bdc5dc3070b59ccbb412a3ad73ff5ca 100644 (file)
@@ -147,6 +147,13 @@ implements Read<Function1<EvaluationContext,Object>> {
     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<EvaluationContext, Object> eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException {
@@ -160,18 +167,20 @@ implements Read<Function1<EvaluationContext,Object>> {
             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);
index c32b0ee240ec0291a21354b365d6317f61664b97..8a73ead7aec28b406bf3acd0a5ec9786d17f61ec 100644 (file)
@@ -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) {
index fd1603d393682211fea293a25c8f92a17c744f33..a3d9074827f57b94f402448c071b6e4e1d07d478 100644 (file)
@@ -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;
index 17ccbe351e0941380bf0b2db94ef9a6a34fc1f96..d6a59a0cbc26a9284e0e656c2062c3b6fae4a2a1 100644 (file)
@@ -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;
index 38bdc706cb88e8b20a959b03d18bfac02ec17de9..09adef2c8088ebb9f6527627c228d4544244c660 100644 (file)
@@ -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;
index e39c73d809120b760110c3476d44368f76e78336..7454a2f3986cfff025242c5d9557876eaa53f809 100644 (file)
@@ -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<IElement> 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<Segment> segments = new ArrayList<Segment>();
+                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) {
index dce4b34ab44e46c7d28ef70b3902b0663ea4fbe6..adff8dab5b9ec87d1271e1ca02d1d3bea58e49a3 100644 (file)
@@ -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<Object, Object> 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 {
 
index 265caa805b604210e12612d49b22a2126ada9c35..912e79cd9ce3e8caec89606ba61c495e797c0923 100644 (file)
@@ -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<Segment> segments = new ArrayList<Segment>();
+                        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<IElement> elements) {
+                    if (sorter != null)
+                        sorter.sort(elements);
+
+                    List<Pair<Double, IElement>> 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<Double, IElement>(distanceSq, element));
+                        }
+                        
+                        if (rgn == null || i == elements.size() - 1) {
+                            Collections.sort(connections, new Comparator<Pair<Double, IElement>>() {
+                                @Override
+                                public int compare(Pair<Double, IElement> connection1, Pair<Double, IElement> connection2) {
+                                    return Double.compare(connection2.first, connection1.first);
+                                }
+                            });
+                            for (Pair<Double, IElement> connection : connections) {
+                                elements.set(tail, connection.second);
+                                tail++;
+                            }
+                            connections.clear();
+                            tail = i + 1;
+                        }
+                    }
+                }
+            }; 
+        }
     }
 
 }
index 4f9df238f4b602ec6e95798570ad2cbbb154f04a..940579ebe82e17ad0f961d8622b57a666869a2a3 100644 (file)
@@ -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<IElement> pickables = new ArrayList<IElement>();
         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<IElement> pick         = new ArrayList<IElement>();
         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<IElement>  picks           = new ArrayList<IElement>();
         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<IElement> sel            = selection.getSelection(me.mouseId);
         IElement      topMostPick    = picks.isEmpty() ? null : picks.get(picks.size() - 1);
         Set<IElement> elementsToDrag = new HashSet<IElement>();
@@ -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;
+                        }
+                    }
                 }
             }
         }
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 72b8232c131040ed9951c7d6159fd09c52d95dc4..bf4577b51e1666378a10b3b6a46e4412b54dab99 100644 (file)
@@ -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;
             }
index 6fc7ac5516f5cc9efb7da5f8cd3a4f588565fb54..c7cc415045c490c7cb2f496a1db7af387a1c8035 100644 (file)
@@ -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.
+ * 
+ * <p>
+ * 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, <code>true</code> must be used if the corresponding TG
+     * file is written e.g. like this:
+     * <pre>
+     * DataContainers.writeFile(location 
+     *     new DataContainer(format,
+     *                       version,
+     *                       metadata,
+     *                       new Variant(TransferableGraph1.BINDING, tg)));
+     * </pre>
+     * 
+     * <p>
+     * On the other hand, any TG files serialized using more optimized methods like
+     * <code>ModelTransferableGraphSource</code> 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.
+     * 
+     * <p>
+     * <code>true</code> 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 <code>input</code> 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<valueLength;i++) {
                        int resource = safeInt();
-                       //idcontext.clear();
+                       if (!shareValueContext)
+                               idcontext.clear();
                        Variant value = (Variant)variantSerializer
                                .deserialize((DataInput)dis, idcontext);
                        values[i] = new Value(resource, value);
index 57b0bd9b86af26ce112aff5101b4bff12be24133..804b35ddc75c2aa1772960ab5a43bb7e32917f5a 100644 (file)
@@ -27,12 +27,12 @@ import org.simantics.db.common.primitiverequest.OrderedSet;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.diagram.adapter.RouteGraphUtils;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
 import org.simantics.diagram.connection.RouteTerminal;
 import org.simantics.diagram.elements.TextNode;
 import org.simantics.diagram.profile.StyleBase;
 import org.simantics.diagram.synchronization.graph.BasicResources;
 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
 import org.simantics.g2d.utils.Alignment;
 import org.simantics.layer0.Layer0;
 import org.simantics.modeling.ModelingResources;
index f29167653f180539fd6ba76858ff47b42961fb63..fbec1abec13d169d982df9251fe89aa2104a33c9 100644 (file)
@@ -18,7 +18,6 @@ import org.simantics.db.common.request.PossibleTypedParent;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
 import org.simantics.diagram.connection.RouteTerminal;
 import org.simantics.diagram.content.ConnectionUtil;
 import org.simantics.diagram.handler.Paster;
@@ -33,6 +32,7 @@ import org.simantics.g2d.element.ElementUtils;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.element.handler.SelectionOutline;
 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
 import org.simantics.modeling.ModelingResources;
 import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.ParentNode;
index 8aef62c7b2dfe3e6e34d9a8629400fbf245ebedf..01960ba1ddfad60a11786a396642a6017f6a4577 100644 (file)
@@ -71,7 +71,6 @@ import org.simantics.scl.runtime.function.Function2;
 import org.simantics.scl.runtime.function.Function4;
 import org.simantics.structural.stubs.StructuralResource2;
 import org.simantics.structural2.utils.StructuralUtils;
-import org.simantics.ui.SimanticsUI;
 import org.simantics.utils.datastructures.Pair;
 import org.simantics.utils.ui.dialogs.ShowMessage;
 
diff --git a/bundles/org.simantics.platform.ui/.classpath b/bundles/org.simantics.platform.ui/.classpath
new file mode 100644 (file)
index 0000000..b862a29
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/bundles/org.simantics.platform.ui/.project b/bundles/org.simantics.platform.ui/.project
new file mode 100644 (file)
index 0000000..f850978
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.platform.ui</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.ManifestBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.SchemaBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.PluginNature</nature>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
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 (file)
index 0000000..f0e776e
--- /dev/null
@@ -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 (file)
index 0000000..41eb6ad
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+               .\r
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 (file)
index 0000000..c7aa2e1
--- /dev/null
@@ -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\\[(?<cmd>.*?(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 (file)
index 0000000..50c4ff5
--- /dev/null
@@ -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 (file)
index 0000000..8c3bf37
--- /dev/null
@@ -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<ILoggingEvent> {
+
+    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
index 9055a8efeca37c1c4822558b77c6ab1c36d242cb..a013dc0419a74ab386d50f5c554fd6190b18e81b 100644 (file)
@@ -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
index 0a2583fc6dd734552999376241b15b509f235086..048514f3a0eaae298b58bdf98149542a8a8fe13b 100644 (file)
@@ -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);
index e50d8498d8d0f048084a95c19edae6f7abd64041..7fd78189fe82c22ece853be8c892c08d9d770494 100644 (file)
@@ -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<T extends ComponentBase<T>> {
 
+    private final transient Logger LOGGER = LoggerFactory.getLogger(getClass());
+    
        abstract public T getConfiguration();
     
     /**
@@ -98,8 +104,13 @@ abstract public class MappingBase<T extends ComponentBase<T>> {
 
     public Map<String, T> getConfigurationBySolverName() {
         Map<String, T> 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<T extends ComponentBase<T>> {
     private void browseConfigurationBySolverName(
             THashMap<String, T> 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<T extends ComponentBase<T>> {
         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<T extends ComponentBase<T>> {
     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<T>() {
index c4b4e341fb85b2aa551612e028a2dafca387f06c..fce1abaa2d8a62fa3fcadfd97c12c66b037e0655 100644 (file)
@@ -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<CompilationContext, Object> {
     
+    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);
+    }
+    
 }
index 5e564cc9114943df427ee1a1bda6877926f335ef..ce824090dce3f686af4a37d77096218f4e9e6f8b 100644 (file)
@@ -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);
+       }
+       
 }
index db6cdac8005aa0bc5de354010e2222b34721988c..95ab9929935addc7a3a5c9ed8fa09fa00b6bcf22 100644 (file)
@@ -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);
 
index cb1ee16d315c28d197016d491130a77d38c98e1c..f5bda18b0a137816c209b12972bfeb346605eb2e 100644 (file)
@@ -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 (file)
index 0000000..d094851
--- /dev/null
@@ -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<Throwable> 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 (file)
index 0000000..4276dc9
--- /dev/null
@@ -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<Consumer<Throwable>>       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<Throwable> handler) {
+        handlers.add(handler);
+    }
+
+    @Override
+    public synchronized void handle(Throwable t) {
+        for (Consumer<Throwable> 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);
+    }
+
+}
index e90980faf6073dcd02400f55fdf818752d91ba45..d8cdbb34652550b203c9833c377fd4baa369043d 100644 (file)
                <module>org.simantics.msvc.runtime.x86_64</module>
                <module>org.simantics.nativemem</module>
                <module>org.simantics.objmap2</module>
+               <module>org.simantics.platform.ui</module>
                <module>org.simantics.platform.ui.ontology</module>
                <module>org.simantics.project</module>
                <module>org.simantics.project.ontology</module>
index 06ab0f00edebc78a556e7a14b527ba394af3a4ac..72baed8b3f4f57d9413fd20002ef602263adc31e 100644 (file)
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.simantics.platform.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>