From: Tuukka Lehtonen Date: Tue, 18 Dec 2018 12:36:45 +0000 (+0200) Subject: Improved Routes view functionality X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F61%2F2561%2F1;p=simantics%2Fdistrict.git Improved Routes view functionality Context menu now has: * Select Route on Diagram (actually works now) * Discard Route * Rename Route Route waypoint names are resolved properly already during creation and show address if defined. Route rename can also be initiated with F2 from the tree. gitlab #25 Change-Id: If5edbe63032b2a5b42a616d4c058d2ea7f3a88c1 --- diff --git a/org.simantics.district.network.ui/META-INF/MANIFEST.MF b/org.simantics.district.network.ui/META-INF/MANIFEST.MF index 2e77a6c4..ccb5ab1d 100644 --- a/org.simantics.district.network.ui/META-INF/MANIFEST.MF +++ b/org.simantics.district.network.ui/META-INF/MANIFEST.MF @@ -30,9 +30,9 @@ Require-Bundle: org.eclipse.e4.ui.model.workbench;bundle-version="1.1.100.v20150 org.eclipse.e4.core.contexts, org.eclipse.jface, org.simantics.scl.osgi, - org.simantics.district.route.ui;bundle-version="1.0.0", org.simantics.district.route Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: javax.annotation;version="1.0.0";resolution:=optional, javax.inject;version="1.0.0" Bundle-ActivationPolicy: lazy +Automatic-Module-Name: org.simantics.district.network.ui diff --git a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/RoutingMode.java b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/RoutingMode.java index d274a617..4f023c88 100644 --- a/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/RoutingMode.java +++ b/org.simantics.district.network.ui/src/org/simantics/district/network/ui/participants/RoutingMode.java @@ -8,6 +8,7 @@ import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.window.Window; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.simantics.db.Resource; @@ -40,6 +41,7 @@ import org.simantics.scenegraph.g2d.nodes.SingleElementNode; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.ui.AdaptionUtils; +import org.simantics.utils.ui.SWTUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -158,8 +160,17 @@ public class RoutingMode extends AbstractMode { return false; KeyPressedEvent kpe = (KeyPressedEvent) e; if (kpe.keyCode == java.awt.event.KeyEvent.VK_ENTER) { - routeService.persistRoute(route); + Route committedRoute = route; route = null; + SWTUtils.asyncExec(Display.getDefault(), () -> { + String newName = askRouteName("Confirm Route", committedRoute.getName()); + if (newName != null) { + committedRoute.setName(newName); + routeService.persistRoute(committedRoute); + } else { + routeService.discardRoute(committedRoute); + } + }); dispose(); } } else if (e instanceof CommandEvent) { @@ -167,20 +178,32 @@ public class RoutingMode extends AbstractMode { if (cmd.equals(Commands.CANCEL)) { return dispose(); } else if (cmd.equals(Commands.RENAME)) { - InputDialog dialog = new InputDialog( - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), - "Rename Route", - "Route name", - route.getName(), - s -> s.trim().length() > 0 ? null : "Name must be non-empty"); - if (dialog.open() == Window.OK) { - route.setName(dialog.getValue()); - } + // TODO: still needs key binding contribution for the district diagram editor + SWTUtils.asyncExec(Display.getDefault(), () -> { + String newName = askRouteName("Rename Route", route.getName()); + if (newName != null) { + route.setName(newName); + routeService.refreshRoute(route); + } + }); } } return false; } + private String askRouteName(String dialogTitle, String initialValue) { + InputDialog dialog = new InputDialog( + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), + dialogTitle, + "Route name", + initialValue, + s -> s.trim().length() > 0 ? null : "Name must be non-empty"); + if (dialog.open() == Window.OK) { + return dialog.getValue(); + } + return null; + } + protected boolean dispose() { setDirty(); remove(); diff --git a/org.simantics.district.region/src/org/simantics/district/region/DiagramRegions.java b/org.simantics.district.region/src/org/simantics/district/region/DiagramRegions.java index 98a3ea4f..175d49c2 100644 --- a/org.simantics.district.region/src/org/simantics/district/region/DiagramRegions.java +++ b/org.simantics.district.region/src/org/simantics/district/region/DiagramRegions.java @@ -7,6 +7,7 @@ import java.awt.geom.PathIterator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -87,6 +88,9 @@ public final class DiagramRegions { } public static Collection findRegions(ReadGraph graph, Resource model) throws DatabaseException { + if (model == null) + return Collections.emptyList(); + List diagrams = QueryIndexUtils.searchByType(graph, model, DiagramResource.getInstance(graph).Diagram); Set regions = new HashSet<>(); diff --git a/org.simantics.district.route.ui/META-INF/MANIFEST.MF b/org.simantics.district.route.ui/META-INF/MANIFEST.MF index 299b2414..3b531e70 100644 --- a/org.simantics.district.route.ui/META-INF/MANIFEST.MF +++ b/org.simantics.district.route.ui/META-INF/MANIFEST.MF @@ -5,16 +5,17 @@ Bundle-SymbolicName: org.simantics.district.route.ui;singleton:=true Bundle-Version: 1.0.0.qualifier Bundle-Activator: org.simantics.district.route.ui.internal.Activator Bundle-Vendor: Semantum Oy -Require-Bundle: org.eclipse.e4.ui.di, - org.eclipse.e4.ui.model.workbench, +Require-Bundle: org.eclipse.e4.core.di, org.eclipse.e4.core.di.annotations, org.eclipse.e4.core.services, + org.eclipse.e4.ui.di, + org.eclipse.e4.ui.model.workbench, + org.eclipse.e4.ui.services, + org.eclipse.e4.ui.workbench, org.simantics.ui, org.simantics.district.route, - org.slf4j.api, - org.eclipse.e4.ui.workbench, - org.eclipse.e4.core.di, - org.eclipse.e4.ui.services + org.simantics.district.network.ui;bundle-version="1.0.0", + org.slf4j.api Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Automatic-Module-Name: fi.vtt.apros.district.route.ui Import-Package: javax.annotation;version="1.0.0";resolution:=optional, diff --git a/org.simantics.district.route.ui/OSGI-INF/l10n/bundle.properties b/org.simantics.district.route.ui/OSGI-INF/l10n/bundle.properties index 6bc47b42..7b710bee 100644 --- a/org.simantics.district.route.ui/OSGI-INF/l10n/bundle.properties +++ b/org.simantics.district.route.ui/OSGI-INF/l10n/bundle.properties @@ -1,4 +1,2 @@ -#Properties file for fi.apros.visualization.table +#Properties file for org.simantics.district.route.ui view.name = Routes -command.commandname.1 = Open Routes View -command.commandname.2 = Select Route on Diagram diff --git a/org.simantics.district.route.ui/fragment.e4xmi b/org.simantics.district.route.ui/fragment.e4xmi index d737ba6b..16c774b3 100644 --- a/org.simantics.district.route.ui/fragment.e4xmi +++ b/org.simantics.district.route.ui/fragment.e4xmi @@ -2,12 +2,16 @@ - - + + + + + + @@ -23,6 +27,8 @@ + + diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteTree.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteTree.java index e58d6a68..04ffc236 100644 --- a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteTree.java +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteTree.java @@ -4,15 +4,26 @@ import java.util.List; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.layout.TreeColumnLayout; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.ColumnViewerEditor; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter; +import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.TreeViewerEditor; +import org.eclipse.jface.viewers.TreeViewerFocusCellManager; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.osgi.util.NLS; @@ -39,7 +50,8 @@ public class RouteTree extends Composite { ESelectionService selectionService; - private final LocalResourceManager resourceManager; + @SuppressWarnings("unused") + private LocalResourceManager resourceManager; private TreeViewer tree; @@ -52,7 +64,7 @@ public class RouteTree extends Composite { } private RouteServiceListener routeServiceListener = e -> { - System.out.println("Route event: " + e); + LOGGER.info("Route event: {}", e); switch (e.type) { case RouteEvent.TYPE_ROUTE_DISCARDED: runUi(() -> tree.refresh()); @@ -77,7 +89,16 @@ public class RouteTree extends Composite { break; case RouteEvent.TYPE_ROUTE_SOURCE_CHANGED: - runUi(() -> tree.setInput(e.service.listRoutes())); + runUi(() -> { + tree.getTree().setRedraw(false); + try { + Object[] expanded = tree.getExpandedElements(); + tree.setInput(e.service.listRoutes()); + tree.setExpandedElements(expanded); + } finally { + tree.getTree().setRedraw(true); + } + }); break; } }; @@ -93,46 +114,71 @@ public class RouteTree extends Composite { private void defaultInitializeUI() { GridDataFactory.fillDefaults().grab(true, true).applyTo(this); - GridLayoutFactory.fillDefaults().numColumns(1).applyTo(this); - createTree(this); } private void createTree(Composite parent) { - tree = new TreeViewer(parent, SWT.SINGLE); + TreeColumnLayout columnLayout = new TreeColumnLayout(false); + parent.setLayout(columnLayout); + + tree = new TreeViewer(parent, SWT.SINGLE | SWT.FULL_SELECTION); tree.setUseHashlookup(true); GridDataFactory.fillDefaults().grab(true, true).applyTo(tree.getControl()); tree.setContentProvider(new ContentProvider()); tree.setLabelProvider(new LabelProvider()); tree.addSelectionChangedListener(this::treeSelectionChanged); - //tree.addPostSelectionChangedListener(this::treeSelectionChanged); - tree.addDoubleClickListener(this::itemDoubleClicked); - // TODO: add route renaming (F2), DnD reordering support - } - private void itemDoubleClicked(DoubleClickEvent e) { - // TODO: default action, i.e. highlight route? - LOGGER.info("double click {}", e); + TreeViewerFocusCellManager focusCellManager = new TreeViewerFocusCellManager(tree, new FocusCellOwnerDrawHighlighter(tree)); + ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(tree) { + @Override + protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) { + boolean singleSelect = tree.getStructuredSelection().size() == 1; + return singleSelect && ( + (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.F2) + || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC + ); + } + }; + + int feature = ColumnViewerEditor.KEYBOARD_ACTIVATION; + + TreeViewerEditor.create(tree, focusCellManager, actSupport, feature); + final TextCellEditor textCellEditor = new TextCellEditor(tree.getTree()); + + TreeViewerColumn viewerColumn = new TreeViewerColumn(tree, SWT.LEFT); + columnLayout.setColumnData(viewerColumn.getColumn(), new ColumnWeightData(1)); + viewerColumn.setLabelProvider(new LabelProvider()); + viewerColumn.setEditingSupport(new EditingSupport(tree) { + @Override + protected void setValue(Object element, Object value) { + LOGGER.info("set value {} for {}", value, element); + Route r = (Route) element; + r.setName((String) value); + Activator.getDefault().getRouteService().persistRoute(r); + getViewer().update(element, null); + } + + @Override + protected Object getValue(Object element) { + return ((Route) element).getName(); + } + + @Override + protected CellEditor getCellEditor(Object element) { + return textCellEditor; + } + + @Override + protected boolean canEdit(Object element) { + return element instanceof Route; + } + }); + + // TODO: add DnD reordering support } private void treeSelectionChanged(SelectionChangedEvent e) { selectionService.setSelection(e.getSelection()); - /* - IStructuredSelection ss = (IStructuredSelection) e.getSelection(); - Object[] arr = ss.toArray(); - if (arr.length == 1) { - Object o = arr[0]; - if (o instanceof Route) { - CompletableFuture> callback = new CompletableFuture<>(); - callback.thenAccept(dnElements -> { - runUi(() -> selectionService.setSelection(new StructuredSelection(dnElements))); - }); - new RouteJob((Route) o, callback).schedule(); - } else if (o instanceof Waypoint) { - selectionService.setSelection(new StructuredSelection(((Waypoint) o).getObject())); - } - } - */ } protected void setInput(List result) { @@ -217,4 +263,11 @@ public class RouteTree extends Composite { return tree.getTree(); } -} + public void editCurrentSelection() { + IStructuredSelection s = tree.getStructuredSelection(); + if (s.size() == 1) { + tree.editElement(s.getFirstElement(), 0); + } + } + +} \ No newline at end of file diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java index 0d99b374..4499782d 100644 --- a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/RouteView.java @@ -71,4 +71,8 @@ public class RouteView { ui.setFocus(); } + public void editCurrentSelection() { + ui.editCurrentSelection(); + } + } \ No newline at end of file diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DiscardRoute.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DiscardRoute.java new file mode 100644 index 00000000..3312b7d3 --- /dev/null +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/DiscardRoute.java @@ -0,0 +1,35 @@ +package org.simantics.district.route.ui.handlers; + +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Shell; +import org.simantics.district.route.Route; +import org.simantics.district.route.ui.internal.Activator; +import org.simantics.utils.ui.ISelectionUtils; + +/** + * @author Tuukka Lehtonen + */ +public class DiscardRoute { + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + return ISelectionUtils.filterSingleSelection(selection, Route.class) != null; + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell activeShell, + @Named(IServiceConstants.ACTIVE_PART) MPart part, + @Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + // get selected route + Route route = ISelectionUtils.filterSingleSelection(selection, Route.class); + if (route != null) + Activator.getDefault().getRouteService().discardRoute(route); + } + +} \ No newline at end of file diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/RenameRoute.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/RenameRoute.java new file mode 100644 index 00000000..d6af0994 --- /dev/null +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/RenameRoute.java @@ -0,0 +1,39 @@ +package org.simantics.district.route.ui.handlers; + +import javax.inject.Named; + +import org.eclipse.e4.core.di.annotations.CanExecute; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.ui.basic.MPart; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Shell; +import org.simantics.district.route.Route; +import org.simantics.district.route.ui.RouteView; +import org.simantics.utils.ui.ISelectionUtils; + +/** + * @author Tuukka Lehtonen + */ +public class RenameRoute { + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + return ISelectionUtils.filterSingleSelection(selection, Route.class) != null; + } + + @Execute + public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell activeShell, + @Named(IServiceConstants.ACTIVE_PART) MPart part, + @Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + // get selected route + Route route = ISelectionUtils.filterSingleSelection(selection, Route.class); + if (route != null) { + Object obj = part.getObject(); + if (obj instanceof RouteView) { + ((RouteView)obj).editCurrentSelection(); + } + } + } + +} \ No newline at end of file diff --git a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/SelectRouteOnDiagram.java b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/SelectRouteOnDiagram.java index 7568ea30..36d14fc6 100644 --- a/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/SelectRouteOnDiagram.java +++ b/org.simantics.district.route.ui/src/org/simantics/district/route/ui/handlers/SelectRouteOnDiagram.java @@ -5,22 +5,37 @@ import java.util.concurrent.CompletableFuture; import javax.inject.Named; +import org.eclipse.e4.core.di.annotations.CanExecute; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.services.IServiceConstants; import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; -import org.simantics.db.layer0.variable.Variable; +import org.simantics.Simantics; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.district.network.ui.DistrictNetworkUIUtil; import org.simantics.district.route.Route; import org.simantics.district.route.RouteJob; import org.simantics.district.route.RouterConfiguration; import org.simantics.utils.ui.ISelectionUtils; +import org.simantics.utils.ui.SWTUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Tuukka Lehtonen */ public class SelectRouteOnDiagram { + private static final Logger LOGGER = LoggerFactory.getLogger(SelectRouteOnDiagram.class); + + @CanExecute + public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION) ISelection selection) { + return ISelectionUtils.filterSingleSelection(selection, Route.class) != null; + } + @Execute public void execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell activeShell, @Named(IServiceConstants.ACTIVE_PART) MPart part, @@ -30,9 +45,32 @@ public class SelectRouteOnDiagram { if (route == null) return; - CompletableFuture> result = new CompletableFuture<>(); + Display display = activeShell.getDisplay(); + CompletableFuture> result = new CompletableFuture<>(); + result.thenAccept(dnElements -> { + try { + openDiagram(display, dnElements); + } catch (DatabaseException e) { + LOGGER.error("Failed to open district network diagram with selection {}", dnElements); + } + }); + RouterConfiguration config = new RouterConfiguration(); new RouteJob(config, route, result).schedule(); } + public static boolean openDiagram(Display display, List dnElements) throws DatabaseException { + if (dnElements.isEmpty()) + return false; + + DistrictNetworkUIUtil.Input in = Simantics.getSession().syncRequest( + new DistrictNetworkUIUtil.ElementToInput(dnElements.get(0))); + if (in == null) + return false; + + SWTUtils.asyncExec(display, + () -> DistrictNetworkUIUtil.openDNDiagramEditorWithSelection(in, dnElements.toArray())); + return true; + } + } \ No newline at end of file diff --git a/org.simantics.district.route/META-INF/MANIFEST.MF b/org.simantics.district.route/META-INF/MANIFEST.MF index 48dce696..118f8157 100644 --- a/org.simantics.district.route/META-INF/MANIFEST.MF +++ b/org.simantics.district.route/META-INF/MANIFEST.MF @@ -15,4 +15,5 @@ Require-Bundle: org.eclipse.core.runtime, Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Automatic-Module-Name: fi.vtt.apros.district.route Bundle-ActivationPolicy: lazy -Export-Package: org.simantics.district.route +Export-Package: org.simantics.district.route, + org.simantics.district.route.internal;x-friends:="org.simantics.district.route.ui" diff --git a/org.simantics.district.route/src/org/simantics/district/route/RouteJob.java b/org.simantics.district.route/src/org/simantics/district/route/RouteJob.java index 84414937..897366ad 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/RouteJob.java +++ b/org.simantics.district.route/src/org/simantics/district/route/RouteJob.java @@ -10,7 +10,6 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.simantics.ObjectIdentitySchedulingRule; import org.simantics.db.Resource; -import org.simantics.db.layer0.variable.Variable; import org.simantics.district.route.internal.Activator; import org.simantics.district.route.internal.RoutePersistence; import org.slf4j.Logger; @@ -26,9 +25,9 @@ public class RouteJob extends Job { private RouterConfiguration config; private List waypoints; - private CompletableFuture> callback; + private CompletableFuture> callback; - public RouteJob(RouterConfiguration config, Route route, CompletableFuture> callback) { + public RouteJob(RouterConfiguration config, Route route, CompletableFuture> callback) { super("Compute route"); Objects.requireNonNull(callback, "Callback must be non-null"); setUser(true); @@ -43,7 +42,7 @@ public class RouteJob extends Job { List routers = Activator.getInstance().getRouteService().routers(); for (Router router : routers) { try { - List path = router.findShortestPath(config, waypoints); + List path = router.findShortestPath(config, waypoints); if (!path.isEmpty()) { callback.complete(path); return Status.OK_STATUS; diff --git a/org.simantics.district.route/src/org/simantics/district/route/RouteService.java b/org.simantics.district.route/src/org/simantics/district/route/RouteService.java index 5c7180b6..5d3f63dc 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/RouteService.java +++ b/org.simantics.district.route/src/org/simantics/district/route/RouteService.java @@ -16,6 +16,8 @@ public interface RouteService { void registerRoute(Route route); + void refreshRoute(Route route); + CompletableFuture persistRoute(Route route); CompletableFuture discardRoute(Route route); diff --git a/org.simantics.district.route/src/org/simantics/district/route/Router.java b/org.simantics.district.route/src/org/simantics/district/route/Router.java index 1d242322..5f73c967 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/Router.java +++ b/org.simantics.district.route/src/org/simantics/district/route/Router.java @@ -3,7 +3,6 @@ package org.simantics.district.route; import java.util.List; import org.simantics.db.Resource; -import org.simantics.db.layer0.variable.Variable; /** * @author Tuukka Lehtonen @@ -20,13 +19,14 @@ public interface Router { /** * Must be invoked outside of any transaction realm, like a database request or * experiment thread, preferably from a background job thread. - * + * + * @param config reserved for future use, may be null * @param wayPoints waypoints for the route to find in visiting order. The * resources must represents district network diagram elements. * @return the piece-wise shortest path between the specified waypoints as a * fully baked path of district network diagram element resources * @throws RoutingException in case of any problems in routing */ - List findShortestPath(RouterConfiguration config, List wayPoints) throws RoutingException; + List findShortestPath(RouterConfiguration config, List wayPoints) throws RoutingException; } diff --git a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteImpl.java b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteImpl.java index 3619b602..224f4af0 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteImpl.java +++ b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteImpl.java @@ -4,13 +4,21 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.simantics.Simantics; import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.request.Read; import org.simantics.district.route.Route; import org.simantics.district.route.RouteEvent; import org.simantics.district.route.Waypoint; +import org.simantics.utils.threads.ThreadUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RouteImpl implements Route { + private static final Logger LOGGER = LoggerFactory.getLogger(RouteImpl.class); + private String name; private Resource model; private Resource backend; @@ -68,8 +76,26 @@ public class RouteImpl implements Route { @Override public Waypoint createWaypoint(Object backend) { - if (backend instanceof Resource) - return new WaypointImpl((Resource) backend, "Point 1"); + if (backend instanceof Resource) { + Resource waypoint = (Resource) backend; + WaypointImpl wp = new WaypointImpl(waypoint, ""); + + // Read real label in background + ThreadUtils.getBlockingWorkExecutor().submit(() -> { + try { + Waypoint p = Simantics.getSession().syncRequest( + (Read) graph -> RoutePersistence.toWaypoint(graph, waypoint)); + if (p != null) { + wp.setLabel(p.getLabel()); + rs.fireEvent(RouteEvent.TYPE_ROUTE_MODIFIED, RouteImpl.this); + } + } catch (DatabaseException e) { + LOGGER.error("Failed to read waypoint {} label", backend, e); + } + }); + + return wp; + } throw new IllegalArgumentException("only Resource type waypoints supported, got " + backend); //$NON-NLS-1$ } diff --git a/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java b/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java index c3ba7571..fceb6612 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java +++ b/org.simantics.district.route/src/org/simantics/district/route/internal/RoutePersistence.java @@ -16,6 +16,7 @@ import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.ListUtils; import org.simantics.db.common.utils.NameUtils; @@ -78,7 +79,7 @@ public class RoutePersistence { Layer0 L0 = Layer0.getInstance(graph); String existingLabel = graph.getPossibleRelatedValue(route, L0.HasLabel, Bindings.STRING); - if (ObjectUtils.objectEquals(existingLabel, label)) { + if (!ObjectUtils.objectEquals(existingLabel, label)) { graph.claimLiteral(route, L0.HasLabel, label, Bindings.STRING); } @@ -124,7 +125,7 @@ public class RoutePersistence { } public static List findRoutes(ReadGraph graph, Resource model) throws DatabaseException { - Resource rf = getRouteFolder(graph, model); + Resource rf = model != null ? getRouteFolder(graph, model) : null; if (rf == null) return Collections.emptyList(); @@ -144,6 +145,16 @@ public class RoutePersistence { return routes; } + public static class ModelRoutesRequest extends ResourceRead> { + public ModelRoutesRequest(Resource model) { + super(model); + } + @Override + public List perform(ReadGraph graph) throws DatabaseException { + return findRoutes(graph, resource); + } + } + public static class ActiveModelRoutesRequest extends UniqueRead> { @Override public List perform(ReadGraph graph) throws DatabaseException { diff --git a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java index e37a803b..6817ea32 100644 --- a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java +++ b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java @@ -11,12 +11,12 @@ import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; -import org.simantics.db.exception.DatabaseException; +import org.simantics.db.common.procedure.adapter.DisposableListener; +import org.simantics.db.common.procedure.adapter.DisposableSyncListener; import org.simantics.db.layer0.request.PossibleActiveModel; import org.simantics.db.management.ISessionContext; import org.simantics.db.management.ISessionContextChangedListener; import org.simantics.db.management.SessionContextChangedEvent; -import org.simantics.db.procedure.SyncListener; import org.simantics.district.route.Route; import org.simantics.district.route.RouteEvent; import org.simantics.district.route.RouteService; @@ -38,42 +38,46 @@ public class RouteServiceImpl implements RouteService, ISessionContextChangedLis private List routers = new ArrayList<>(); private List unmodifiableRouters = Collections.unmodifiableList(routers); - private class StoreListener implements SyncListener { - private boolean disposed = false; - + private class StoreRoutesListener extends DisposableListener> { @Override - public void execute(ReadGraph graph, Resource activeModel) { - try { - if (activeModel != null) { - resetRoutes(RoutePersistence.findRoutes(graph, activeModel)); - } else { - resetRoutes(Collections.emptyList()); - } - fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, this); - } catch (DatabaseException e) { - LOGGER.error("Failed to read routes from model {}", activeModel, e); - } + public void execute(List result) { + resetRoutes(result); + fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, RouteServiceImpl.this); } @Override - public void exception(ReadGraph graph, Throwable t) { - LOGGER.error("Failed to listen to current route service storage", t); + public void exception(Throwable t) { + LOGGER.error("Failed to listen to current route store routes", t); } + } - public void dispose() { - disposed = true; + private class StoreListener extends DisposableSyncListener { + @Override + public void execute(ReadGraph graph, Resource activeModel) { + if (activeModel != null) { + StoreRoutesListener srl = storeRoutesListener; + if (srl != null) + srl.dispose(); + Simantics.getSession().asyncRequest( + new RoutePersistence.ModelRoutesRequest(activeModel), + storeRoutesListener = new StoreRoutesListener()); + } else { + resetRoutes(Collections.emptyList()); + fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, RouteServiceImpl.this); + } } @Override - public boolean isDisposed() { - return disposed; + public void exception(ReadGraph graph, Throwable t) { + LOGGER.error("Failed to listen to current route service storage", t); } } + private StoreRoutesListener storeRoutesListener; private StoreListener storeListener; private synchronized void listenToActiveModels(Session s) { - StoreListener sl = storeListener; + StoreListener sl = storeListener; if (sl != null) sl.dispose(); if (s != null) { @@ -133,6 +137,11 @@ public class RouteServiceImpl implements RouteService, ISessionContextChangedLis fireEvent(RouteEvent.TYPE_ROUTE_REGISTERED, route); } + @Override + public void refreshRoute(Route route) { + fireEvent(RouteEvent.TYPE_ROUTE_MODIFIED, route); + } + @Override public CompletableFuture persistRoute(Route route) { fireEvent(RouteEvent.TYPE_ROUTE_PERSISTING, route);