package org.simantics.district.route.ui; import java.util.List; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; import org.eclipse.jface.layout.GridDataFactory; 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.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; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Tree; import org.simantics.district.route.Route; import org.simantics.district.route.RouteEvent; 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; @SuppressWarnings("unused") private 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 -> { LOGGER.info("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.getTree().setRedraw(false); try { Object[] expanded = tree.getExpandedElements(); tree.setInput(e.service.listRoutes()); tree.setExpandedElements(expanded); } finally { tree.getTree().setRedraw(true); } }); 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); createTree(this); } private void createTree(Composite parent) { 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); 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()); } 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)); } } public Tree getTree() { return tree.getTree(); } public void editCurrentSelection() { IStructuredSelection s = tree.getStructuredSelection(); if (s.size() == 1) { tree.editElement(s.getFirstElement(), 0); } } }