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> 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) { 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)); } } }