X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FGraphExplorerImpl.java;h=6361401b48723f58aa62516387421f5f473ea8cc;hb=refs%2Fchanges%2F81%2F3081%2F1;hp=df4860d42145551c2fd59c428ae7c749763a5a3d;hpb=1efdd1cea0fcef60df0267f23e4ffda9fab5b23d;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 df4860d42..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;
@@ -289,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;
/**
@@ -310,11 +328,11 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
Tree tree;
@SuppressWarnings({ "rawtypes" })
- final HashMap, NodeQueryProcessor> processors = new HashMap<>();
+ final HashMap, NodeQueryProcessor> processors = new HashMap, NodeQueryProcessor>();
@SuppressWarnings({ "rawtypes" })
- final HashMap
*/
- 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;
@@ -519,12 +537,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;
@@ -552,12 +570,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)}
@@ -631,7 +649,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
@@ -695,7 +713,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
updateCounter = 0;
uiUpdateScheduler.schedule(this, 50, TimeUnit.MILLISECONDS);
} else {
- tree.getDisplay().asyncExec(new UpdateRunner(GraphExplorerImpl.this));
+ tree.getDisplay().asyncExec(new UpdateRunner(GraphExplorerImpl.this, GraphExplorerImpl.this.explorerContext));
}
}
@@ -770,7 +788,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;
@@ -1058,7 +1076,6 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
editor.setEditor(combo, item, columnIndex);
combo.setFocus();
- combo.setListVisible(true);
GraphExplorerImpl.this.reconfigureTreeEditorForText(item, columnIndex, combo, combo.getText(), SWT.DEFAULT, 0, 0);
@@ -1067,6 +1084,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
// Removed in comboListener
currentlyModifiedNodes.add(context);
+ combo.setListVisible(true);
//System.out.println("START ENUMERATION EDITING: " + item);
}
@@ -1363,14 +1381,17 @@ 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, 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, 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;
+ }
}
});
@@ -1472,33 +1493,39 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
};
tree.addListener(SWT.MouseDown, mouseEditListener);
tree.addListener(SWT.DragDetect, mouseEditListener);
- tree.addListener(SWT.DragDetect, 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 {
- resetSelectionFromWidget();
- }));
- os.addPostSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
- //System.out.println("OPENSTRATEGY: post selection changed: " + e);
- resetSelectionFromWidgetAndFirePostSelection(true);
- }));
+ // Add a tree selection listener for keeping the selection of
+ // GraphExplorer's ISelectionProvider up-to-date.
+ tree.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ widgetSelectionChanged(false);
+ }
+ });
// This listener takes care of updating the set of currently selected
// TreeItem instances. This set is needed because we need to know in
@@ -1581,33 +1612,107 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
}
/**
- * @return the new selection if it was different from the old selection in
- * {@link #selectionProvider}
+ * Mod count for delaying post selection changed events.
*/
- private ISelection resetSelectionFromWidget() {
- ISelection widgetSelection = getWidgetSelection();
-// System.out.println("resetSelection()");
-// System.out.println(" provider selection: " + selectionProvider.getSelection());
-// System.out.println(" widget selection: " + widgetSelection);
- boolean equals = selectionProvider.selectionEquals(widgetSelection);
- selectionProvider.setSelectionWithoutFiring(widgetSelection);
- return equals ? null : widgetSelection;
- }
+ int postSelectionModCount = 0;
+
+ /**
+ * Last tree selection modification time for implementing a quiet
+ * time for selection changes.
+ */
+ long lastSelectionModTime = System.currentTimeMillis() - 10000;
/**
- * @return the new selection if it was different from the old selection in
- * {@link #selectionProvider}
+ * Current target time for the selection to be set. Calculated
+ * according to the set quiet time and last selection modification
+ * time.
*/
- private boolean resetSelectionFromWidgetAndFirePostSelection(boolean force) {
- ISelection s = resetSelectionFromWidget();
- boolean fire = s != null || force;
- if (fire) {
- //System.out.println("FIRING POST-SELECTION: " + selectionProvider.getSelection());
- selectionProvider.firePostSelection(selectionProvider.getSelection());
+ 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();
+ }
}
- 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.
@@ -2322,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);
}
});
}
@@ -3014,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);