X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FGraphExplorerImpl.java;h=0168ad864ae5e98258498d76fef7f04f42ee4282;hp=0e45f86ca4843fe53233ecec6217a07026ee4954;hb=270834ce3962a4bca3945d06e642a99d21688c16;hpb=e84008c029ef2336cf55ad371256c9a12a889e98 diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java index 0e45f86ca..0168ad864 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java @@ -39,7 +39,6 @@ import org.eclipse.core.runtime.AssertionFailedException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.IStatusLineManager; @@ -51,6 +50,7 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; +import org.eclipse.jface.util.OpenStrategy; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; @@ -69,7 +69,6 @@ import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; @@ -114,6 +113,7 @@ import org.simantics.browsing.ui.PrimitiveQueryProcessor; import org.simantics.browsing.ui.SelectionDataResolver; import org.simantics.browsing.ui.SelectionFilter; import org.simantics.browsing.ui.StatePersistor; +import org.simantics.browsing.ui.common.AdaptableHintContext; import org.simantics.browsing.ui.common.ColumnKeys; import org.simantics.browsing.ui.common.ErrorLogger; import org.simantics.browsing.ui.common.NodeContextBuilder; @@ -144,6 +144,7 @@ import org.simantics.browsing.ui.common.processors.DefaultViewpointProcessor; import org.simantics.browsing.ui.common.processors.IsExpandedProcessor; import org.simantics.browsing.ui.common.processors.NoSelectionRequestProcessor; import org.simantics.browsing.ui.common.processors.ProcessorLifecycle; +import org.simantics.browsing.ui.common.state.ExplorerStates; import org.simantics.browsing.ui.content.ImageDecorator; import org.simantics.browsing.ui.content.Imager; import org.simantics.browsing.ui.content.LabelDecorator; @@ -168,6 +169,7 @@ import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.SWTThread; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.ISelectionUtils; +import org.simantics.utils.ui.SWTUtils; import org.simantics.utils.ui.jface.BasePostSelectionProvider; import org.simantics.utils.ui.widgets.VetoingEventHandler; import org.simantics.utils.ui.workbench.WorkbenchUtils; @@ -287,24 +289,6 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph public static final int DEFAULT_MAX_CHILDREN = 1000; - private static final long POST_SELECTION_DELAY = 300; - - /** - * The time in milliseconds that must elapse between consecutive - * {@link Tree} {@link SelectionListener#widgetSelected(SelectionEvent)} - * invocations in order for this class to construct a new selection. - * - *

- * This is done because selection construction can be very expensive as the - * selected set grows larger when the user is pressing shift+arrow keys. - * GraphExplorerImpl will naturally listen to all changes in the tree - * selection, but as an optimization will not construct new - * StructuredSelection instances for every selection change event. A new - * selection will be constructed and set only if the selection hasn't - * changed for the amount of milliseconds specified by this constant. - */ - private static final long SELECTION_CHANGE_QUIET_TIME = 150; - private final IThreadWorkQueue thread; /** @@ -326,11 +310,11 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph Tree tree; @SuppressWarnings({ "rawtypes" }) - final HashMap, NodeQueryProcessor> processors = new HashMap, NodeQueryProcessor>(); + final HashMap, NodeQueryProcessor> processors = new HashMap<>(); @SuppressWarnings({ "rawtypes" }) - final HashMap primitiveProcessors = new HashMap(); + final HashMap primitiveProcessors = new HashMap<>(); @SuppressWarnings({ "rawtypes" }) - final HashMap dataSources = new HashMap(); + final HashMap dataSources = new HashMap<>(); class GraphExplorerContext extends AbstractDisposable implements IGraphExplorerContext { // This is for query debugging only. @@ -349,7 +333,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph * null there's nothing scheduled yet in which case * scheduling can commence. Otherwise the update should be skipped. */ - AtomicReference currentQueryUpdater = new AtomicReference(); + AtomicReference currentQueryUpdater = new AtomicReference<>(); /** * Keeps track of nodes that have already been auto-expanded. After @@ -357,7 +341,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph * expanded state after that. This makes it possible for the user to * close auto-expanded nodes. */ - Map autoExpanded = new WeakHashMap(); + Map autoExpanded = new WeakHashMap<>(); @Override @@ -480,7 +464,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph GraphExplorerContext explorerContext = new GraphExplorerContext(); - HashSet pendingItems = new HashSet(); + HashSet pendingItems = new HashSet<>(); boolean updating = false; boolean pendingRoot = false; @@ -501,14 +485,14 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph * Access this map only in the SWT thread to keep it thread-safe. *

*/ - BijectionMap contextToItem = new BijectionMap(); + BijectionMap contextToItem = new BijectionMap<>(); /** * Columns of the UI viewer. Use {@link #setColumns(Column[])} to * initialize. */ Column[] columns = new Column[0]; - Map columnKeyToIndex = new HashMap(); + Map columnKeyToIndex = new HashMap<>(); boolean refreshingColumnSizes = false; boolean columnsAreVisible = true; @@ -535,12 +519,12 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph /** Set to true when the Tree widget is disposed. */ private boolean disposed = false; - private final CopyOnWriteArrayList focusListeners = new CopyOnWriteArrayList(); - private final CopyOnWriteArrayList mouseListeners = new CopyOnWriteArrayList(); - private final CopyOnWriteArrayList keyListeners = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList focusListeners = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList mouseListeners = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList keyListeners = new CopyOnWriteArrayList<>(); /** Selection provider */ - private GraphExplorerPostSelectionProvider postSelectionProvider = new GraphExplorerPostSelectionProvider(this); + private GraphExplorerPostSelectionProvider postSelectionProvider = new GraphExplorerPostSelectionProvider(this); protected BasePostSelectionProvider selectionProvider = new BasePostSelectionProvider(); protected SelectionDataResolver selectionDataResolver; protected SelectionFilter selectionFilter; @@ -568,12 +552,12 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph * item was a part of the current selection in which case the selection must * be updated. */ - private final Map selectedItems = new HashMap(); + private final Map selectedItems = new HashMap<>(); /** * TODO: specify what this is for */ - private final Set selectionRefreshContexts = new HashSet(); + private final Set selectionRefreshContexts = new HashSet<>(); /** * If this field is non-null, it means that if {@link #setData(Event)} @@ -647,7 +631,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph * * @see #setPendingImages(IProgressMonitor) */ - Map imageTasks = new THashMap(); + Map imageTasks = new THashMap<>(); /** * A state flag indicating whether the vertical scroll bar was visible for @@ -711,7 +695,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph updateCounter = 0; uiUpdateScheduler.schedule(this, 50, TimeUnit.MILLISECONDS); } else { - tree.getDisplay().asyncExec(new UpdateRunner(GraphExplorerImpl.this, GraphExplorerImpl.this.explorerContext)); + tree.getDisplay().asyncExec(new UpdateRunner(GraphExplorerImpl.this)); } } @@ -786,7 +770,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph * modified. These are used internally to prevent duplicate edits from being * initiated which should always be a sensible thing to do. */ - private Set currentlyModifiedNodes = new THashSet(); + private Set currentlyModifiedNodes = new THashSet<>(); private final TreeEditor editor; private Color invalidModificationColor = null; @@ -1379,17 +1363,14 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph // Keep track of the previous single selection to help // decide whether to start editing a tree node on mouse // downs or not. - tree.addListener(SWT.Selection, new Listener() { - @Override - public void handleEvent(Event event) { - TreeItem[] selection = tree.getSelection(); - if (selection.length == 1) { - //for (TreeItem item : selection) - // System.out.println("selection: " + item); - previousSingleSelection = selection[0]; - } else { - previousSingleSelection = null; - } + tree.addListener(SWT.Selection, event -> { + TreeItem[] selection = tree.getSelection(); + if (selection.length == 1) { + //for (TreeItem item : selection) + // System.out.println("selection: " + item); + previousSingleSelection = selection[0]; + } else { + previousSingleSelection = null; } }); @@ -1491,39 +1472,33 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph }; tree.addListener(SWT.MouseDown, mouseEditListener); tree.addListener(SWT.DragDetect, mouseEditListener); - tree.addListener(SWT.DragDetect, new Listener() { - @Override - public void handleEvent(Event event) { - Point test = new Point(event.x, event.y); - TreeItem item = tree.getItem(test); - if(item != null) { - for(int i=0;i { + Point test = new Point(event.x, event.y); + TreeItem item = tree.getItem(test); + if(item != null) { + for(int i=0;i { + Point test = new Point(event.x, event.y); + TreeItem item = tree.getItem(test); + if(item != null) { + for(int i=0;i { + //System.out.println("OPENSTRATEGY: post selection changed: " + e); + resetSelection(); + selectionProvider.firePostSelection(selectionProvider.getSelection()); + })); // This listener takes care of updating the set of currently selected // TreeItem instances. This set is needed because we need to know in @@ -1609,108 +1578,14 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph }); } - /** - * Mod count for delaying post selection changed events. - */ - int postSelectionModCount = 0; - - /** - * Last tree selection modification time for implementing a quiet - * time for selection changes. - */ - long lastSelectionModTime = System.currentTimeMillis() - 10000; - - /** - * Current target time for the selection to be set. Calculated - * according to the set quiet time and last selection modification - * time. - */ - long selectionSetTargetTime = 0; - - /** - * true if delayed selection runnable is current scheduled or - * running. - */ - boolean delayedSelectionScheduled = false; - - Runnable SELECTION_DELAY = new Runnable() { - @Override - public void run() { - if (tree.isDisposed()) - return; - long now = System.currentTimeMillis(); - long waitTimeLeft = selectionSetTargetTime - now; - if (waitTimeLeft > 0) { - // Not enough quiet time, reschedule. - delayedSelectionScheduled = true; - tree.getDisplay().timerExec((int) waitTimeLeft, this); - } else { - // Time to perform selection, stop rescheduling. - delayedSelectionScheduled = false; - resetSelection(); - } - } - }; - - private void widgetSelectionChanged(boolean forceSelectionChange) { - long modTime = System.currentTimeMillis(); - long delta = modTime - lastSelectionModTime; - lastSelectionModTime = modTime; - if (!forceSelectionChange && delta < SELECTION_CHANGE_QUIET_TIME) { - long msToWait = SELECTION_CHANGE_QUIET_TIME - delta; - selectionSetTargetTime = modTime + msToWait; - if (!delayedSelectionScheduled) { - delayedSelectionScheduled = true; - tree.getDisplay().timerExec((int) msToWait, SELECTION_DELAY); - } - // Make sure that post selection change events do not fire. - ++postSelectionModCount; - return; - } - - // Immediate selection reconstruction. - resetSelection(); - } - private void resetSelection() { final ISelection selection = getWidgetSelection(); - - //System.out.println("resetSelection(" + postSelectionModCount + ")"); - //System.out.println(" provider selection: " + selectionProvider.getSelection()); - //System.out.println(" widget selection: " + selection); - +// System.out.println("resetSelection()"); +// System.out.println(" provider selection: " + selectionProvider.getSelection()); +// System.out.println(" widget selection: " + selection); selectionProvider.setAndFireNonEqualSelection(selection); - - // Implement deferred firing of post selection events - final int count = ++postSelectionModCount; - //System.out.println("[" + System.currentTimeMillis() + "] scheduling postSelectionChanged " + count + ": " + selection); - ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() { - @Override - public void run() { - int newCount = postSelectionModCount; - // Don't publish selection yet, there's another change incoming. - //System.out.println("[" + System.currentTimeMillis() + "] checking post selection publish: " + count + " vs. " + newCount + ": " + selection); - if (newCount != count) - return; - //System.out.println("[" + System.currentTimeMillis() + "] " + count + " count equals, firing post selection listeners: " + selection); - - if (tree.isDisposed()) - return; - - //System.out.println("scheduling fire post selection changed: " + selection); - tree.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if (tree.isDisposed() || selectionProvider == null) - return; - //System.out.println("firing post selection changed: " + selection); - selectionProvider.firePostSelection(selection); - } - }); - } - }, POST_SELECTION_DELAY, TimeUnit.MILLISECONDS); } - + protected void setDefaultProcessors() { // Add a simple IMAGER query processor that always returns null. // With this processor no images will ever be shown. @@ -1882,15 +1757,15 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph }); } } - + private void initializeState() { if (persistor == null) return; + ExplorerStates.scheduleRead(getRoot(), persistor) + .thenAccept(state -> SWTUtils.asyncExec(tree, () -> restoreState(state))); + } - ExplorerState state = persistor.deserialize( - Platform.getStateLocation(Activator.getDefault().getBundle()).toFile(), - getRoot()); - + private void restoreState(ExplorerState state) { // topNodeToSet will be processed by #setData when it encounters a // NodeContext that matches this one. // topNodePath = state.topNodePath; @@ -1901,7 +1776,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph if (processor instanceof DefaultIsExpandedProcessor) { DefaultIsExpandedProcessor isExpandedProcessor = (DefaultIsExpandedProcessor)processor; for(NodeContext expanded : state.expandedNodes) { - isExpandedProcessor.setExpanded(expanded, true); + isExpandedProcessor.replaceExpanded(expanded, true); } } } @@ -1946,7 +1821,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph } persistor.serialize( - Platform.getStateLocation(Activator.getDefault().getBundle()).toFile(), + ExplorerStates.explorerStateLocation(), getRoot(), new ExplorerState(topNodePath, topNodePathChildIndex, expandedNodes, columnWidths)); } @@ -2425,7 +2300,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph // System.out.println("MODCOUNT: " + modCount + " vs. " + count); if (modCount != count) return; - widgetSelectionChanged(true); + resetSelection(); } }); } @@ -3250,13 +3125,16 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph */ private void doSetColumns(Column[] cols, Consumer> callback) { // Attempt to keep previous column widths. - Map prevWidths = new HashMap(); + Map prevWidths = new HashMap<>(); for (TreeColumn column : tree.getColumns()) { - prevWidths.put(column.getText(), column.getWidth()); - column.dispose(); + Column c = (Column) column.getData(); + if (c != null) { + prevWidths.put(c.getKey(), column.getWidth()); + column.dispose(); + } } - HashMap keyToIndex = new HashMap(); + HashMap keyToIndex = new HashMap<>(); for (int i = 0; i < cols.length; ++i) { keyToIndex.put(cols[i].getKey(), i); } @@ -3267,7 +3145,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph this.columnImageArray = new Image[cols.length]; this.columnDescOrImageArray = new Object[cols.length]; - Map map = new HashMap(); + Map map = new HashMap<>(); tree.setHeaderVisible(columnsAreVisible); for (Column column : columns) { @@ -3280,7 +3158,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph int cw = column.getWidth(); // Try to keep previous widths - Integer w = prevWidths.get(column); + Integer w = prevWidths.get(column.getKey()); if (w != null) c.setWidth(w); else if (cw != Column.DEFAULT_CONTROL_WIDTH) @@ -3305,13 +3183,9 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph if(callback != null) callback.accept(map); // Make sure the explorer fits the columns properly after initialization. - tree.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if (tree.isDisposed()) - return; + SWTUtils.asyncExec(tree, () -> { + if (!tree.isDisposed()) refreshColumnSizes(); - } }); }