--- /dev/null
+package org.simantics.district.route.ui;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.simantics.db.Resource;
+import org.simantics.district.route.Route;
+import org.simantics.district.route.RouteEvent;
+import org.simantics.district.route.RouteJob;
+import org.simantics.district.route.RouteService;
+import org.simantics.district.route.RouteServiceListener;
+import org.simantics.district.route.Waypoint;
+import org.simantics.district.route.ui.internal.Activator;
+import org.simantics.utils.ui.SWTUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 6.09
+ */
+public class RouteTree extends Composite {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RouteTree.class);
+
+ ESelectionService selectionService;
+
+ private final LocalResourceManager resourceManager;
+
+ private TreeViewer tree;
+
+ public RouteTree(Composite parent, int style, ESelectionService selectionService) {
+ super(parent, style);
+ this.selectionService = selectionService;
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
+ defaultInitializeUI();
+ trackRouteInput();
+ }
+
+ private RouteServiceListener routeServiceListener = e -> {
+ System.out.println("Route event: " + e);
+ switch (e.type) {
+ case RouteEvent.TYPE_ROUTE_DISCARDED:
+ runUi(() -> tree.refresh());
+ break;
+
+ case RouteEvent.TYPE_ROUTE_REGISTERED:
+ runUi(() -> {
+ tree.refresh();
+ tree.setExpandedState(e.obj, true);
+ });
+ break;
+
+ case RouteEvent.TYPE_ROUTE_MODIFIED:
+ runUi(() -> {
+ tree.refresh(e.obj);
+ tree.setExpandedState(e.obj, true);
+ });
+ break;
+
+ case RouteEvent.TYPE_ROUTE_RENAMED:
+ runUi(() -> tree.refresh(e.obj));
+ break;
+
+ case RouteEvent.TYPE_ROUTE_SOURCE_CHANGED:
+ runUi(() -> tree.setInput(e.service.listRoutes()));
+ break;
+ }
+ };
+
+ private void trackRouteInput() {
+ RouteService rs = Activator.getDefault().getRouteService();
+ if (rs != null) {
+ rs.addListener(routeServiceListener);
+ addDisposeListener(e -> rs.removeListener(routeServiceListener));
+ setInput(rs.listRoutes());
+ }
+ }
+
+ 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);
+ 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);
+ }
+
+ 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<List<Resource>> 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).<Object>getObject()));
+ }
+ }
+ */
+ }
+
+ protected void setInput(List<Route> result) {
+ runUi(() -> {
+ TreePath[] paths = tree.getExpandedTreePaths();
+ tree.setInput(result);
+ tree.setExpandedTreePaths(paths);
+ });
+ }
+
+ protected void runUi(Runnable r) {
+ SWTUtils.asyncExec(getDisplay(), () -> {
+ if (!tree.getControl().isDisposed())
+ r.run();
+ });
+ }
+
+ private static class ContentProvider implements ITreeContentProvider {
+
+ private static final Object[] NONE = {};
+ @SuppressWarnings("unused")
+ private List<?> input;
+
+ @Override
+ public void dispose() {
+ input = null;
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ input = newInput instanceof List ? (List<?>) newInput : null;
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof List)
+ return ((List<?>) inputElement).toArray();
+ return NONE;
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof Route) {
+ return ((Route) parentElement).waypoints().toArray();
+ }
+ return NONE;
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ if (element instanceof Route) {
+ return !((Route) element).waypoints().isEmpty();
+ }
+ return false;
+ }
+
+ }
+
+ private static class LabelProvider extends ColumnLabelProvider {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof Route) {
+ Route r = (Route) element;
+ return NLS.bind("{0} ({1} waypoints)", r.getName(), r.count());
+ } else if (element instanceof Waypoint) {
+ }
+ return element.toString();
+ }
+ @Override
+ public void update(ViewerCell cell) {
+ Object element = cell.getElement();
+ cell.setText(getText(element));
+ }
+ }
+
+}