X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FGraphExplorerImpl.java;h=6361401b48723f58aa62516387421f5f473ea8cc;hb=2d2faa8fa89c174e70dc5bd63fb5d56acc1925c1;hp=e90ae5eeecf78dee9d9bd0f6a74084e116d83af9;hpb=9cfc6c61008c1b6b278bac6c8585df478dddf55f;p=simantics%2Fplatform.git 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 e90ae5eee..6361401b4 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 @@ -50,7 +50,6 @@ 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,6 +68,7 @@ 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; @@ -173,8 +173,6 @@ 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; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectProcedure; @@ -187,8 +185,6 @@ import gnu.trove.set.hash.THashSet; */ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, GraphExplorer /*, IPostSelectionProvider*/ { - private static final Logger LOGGER = LoggerFactory.getLogger(GraphExplorerImpl.class); - private static class GraphExplorerPostSelectionProvider implements IPostSelectionProvider { private GraphExplorerImpl ge; @@ -293,6 +289,24 @@ 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;
/**
@@ -314,11 +328,11 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
Tree tree;
@SuppressWarnings({ "rawtypes" })
- final HashMap
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();
+ }
}
- return fire;
+ };
+
+ 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);
+
+ 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.
@@ -2330,7 +2427,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
// System.out.println("MODCOUNT: " + modCount + " vs. " + count);
if (modCount != count)
return;
- resetSelectionFromWidgetAndFirePostSelection(false);
+ widgetSelectionChanged(true);
}
});
}
@@ -3022,7 +3119,10 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
//System.out.println("NodeContext path : " + contexts);
NodeContext head = tryFind(contexts[position]);
-
+ // tryFind may return null for positions, that actually have NodeContext.
+ if (head == null)
+ return false;
+
if(position == contexts.length-1) {
return select(head);