]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java
Usability fixes for GraphExplorerImpl -related WB selection propagation
[simantics/platform.git] / bundles / org.simantics.browsing.ui.swt / src / org / simantics / browsing / ui / swt / GraphExplorerImpl.java
index df736c1e6dc2603315bea23f47b46a2170a52f55..0168ad864ae5e98258498d76fef7f04f42ee4282 100644 (file)
@@ -50,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;
@@ -68,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;
@@ -289,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.
-     * 
-     * <p>
-     * 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;
 
     /**
@@ -328,11 +310,11 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
     Tree                                           tree;
 
     @SuppressWarnings({ "rawtypes" })
-    final HashMap<CacheKey<?>, NodeQueryProcessor> processors            = new HashMap<CacheKey<?>, NodeQueryProcessor>();
+    final HashMap<CacheKey<?>, NodeQueryProcessor> processors            = new HashMap<>();
     @SuppressWarnings({ "rawtypes" })
-    final HashMap<Object, PrimitiveQueryProcessor> primitiveProcessors   = new HashMap<Object, PrimitiveQueryProcessor>();
+    final HashMap<Object, PrimitiveQueryProcessor> primitiveProcessors   = new HashMap<>();
     @SuppressWarnings({ "rawtypes" })
-    final HashMap<Class, DataSource>               dataSources           = new HashMap<Class, DataSource>();
+    final HashMap<Class, DataSource>               dataSources           = new HashMap<>();
     
     class GraphExplorerContext extends AbstractDisposable implements IGraphExplorerContext {
         // This is for query debugging only.
@@ -351,7 +333,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
          * <code>null</code> there's nothing scheduled yet in which case
          * scheduling can commence. Otherwise the update should be skipped.
          */
-        AtomicReference<Runnable> currentQueryUpdater = new AtomicReference<Runnable>();
+        AtomicReference<Runnable> currentQueryUpdater = new AtomicReference<>();
 
         /**
          * Keeps track of nodes that have already been auto-expanded. After
@@ -359,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<NodeContext, Boolean>     autoExpanded  = new WeakHashMap<NodeContext, Boolean>();
+        Map<NodeContext, Boolean>     autoExpanded  = new WeakHashMap<>();
 
         
         @Override
@@ -482,7 +464,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
 
     GraphExplorerContext                         explorerContext     = new GraphExplorerContext();
 
-    HashSet<TreeItem>                            pendingItems        = new HashSet<TreeItem>();
+    HashSet<TreeItem>                            pendingItems        = new HashSet<>();
     boolean                                      updating            = false;
     boolean                                      pendingRoot         = false;
 
@@ -503,14 +485,14 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
      * Access this map only in the SWT thread to keep it thread-safe.
      * </p>
      */
-    BijectionMap<NodeContext, TreeItem>         contextToItem     = new BijectionMap<NodeContext, TreeItem>();
+    BijectionMap<NodeContext, TreeItem>         contextToItem     = new BijectionMap<>();
 
     /**
      * Columns of the UI viewer. Use {@link #setColumns(Column[])} to
      * initialize.
      */
     Column[]                                     columns           = new Column[0];
-    Map<String, Integer>                         columnKeyToIndex  = new HashMap<String, Integer>();
+    Map<String, Integer>                         columnKeyToIndex  = new HashMap<>();
     boolean                                      refreshingColumnSizes = false;
     boolean                                      columnsAreVisible = true;
 
@@ -537,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<FocusListener>  focusListeners           = new CopyOnWriteArrayList<FocusListener>();
-    private final CopyOnWriteArrayList<MouseListener>  mouseListeners           = new CopyOnWriteArrayList<MouseListener>();
-    private final CopyOnWriteArrayList<KeyListener>    keyListeners             = new CopyOnWriteArrayList<KeyListener>();
+    private final CopyOnWriteArrayList<FocusListener>  focusListeners     = new CopyOnWriteArrayList<>();
+    private final CopyOnWriteArrayList<MouseListener>  mouseListeners     = new CopyOnWriteArrayList<>();
+    private final CopyOnWriteArrayList<KeyListener>    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;
@@ -570,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<TreeItem, NodeContext>     selectedItems            = new HashMap<TreeItem, NodeContext>();
+    private final Map<TreeItem, NodeContext>     selectedItems            = new HashMap<>();
 
     /**
      * TODO: specify what this is for
      */
-    private final Set<NodeContext>               selectionRefreshContexts = new HashSet<NodeContext>();
+    private final Set<NodeContext>               selectionRefreshContexts = new HashSet<>();
 
     /**
      * If this field is non-null, it means that if {@link #setData(Event)}
@@ -649,7 +631,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
      * 
      * @see #setPendingImages(IProgressMonitor)
      */
-    Map<TreeItem, ImageTask> imageTasks     = new THashMap<TreeItem, ImageTask>();
+    Map<TreeItem, ImageTask> imageTasks     = new THashMap<>();
 
     /**
      * A state flag indicating whether the vertical scroll bar was visible for
@@ -713,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));
                     }
                     
                 }
@@ -788,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<NodeContext> currentlyModifiedNodes   = new THashSet<NodeContext>();
+    private Set<NodeContext> currentlyModifiedNodes   = new THashSet<>();
 
     private final TreeEditor editor;
     private Color            invalidModificationColor = null;
@@ -1381,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;
             }
         });
 
@@ -1493,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<tree.getColumnCount();i++) {
-                        Rectangle rect = item.getBounds(i);
-                        if(rect.contains(test)) {
-                            tree.setData(KEY_DRAG_COLUMN, i);
-                            return;
-                        }
+        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<tree.getColumnCount();i++) {
+                    Rectangle rect = item.getBounds(i);
+                    if(rect.contains(test)) {
+                        tree.setData(KEY_DRAG_COLUMN, i);
+                        return;
                     }
                 }
-                tree.setData(KEY_DRAG_COLUMN, -1);
             }
+            tree.setData(KEY_DRAG_COLUMN, -1);
         });
-        tree.addListener(SWT.MouseMove, 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<tree.getColumnCount();i++) {
-                        Rectangle rect = item.getBounds(i);
-                        if(rect.contains(test)) {
-                               transientState.setActiveColumn(i);
-                            return;
-                        }
+        tree.addListener(SWT.MouseMove, event -> {
+            Point test = new Point(event.x, event.y);
+            TreeItem item = tree.getItem(test);
+            if(item != null) {
+                for(int i=0;i<tree.getColumnCount();i++) {
+                    Rectangle rect = item.getBounds(i);
+                    if(rect.contains(test)) {
+                        transientState.setActiveColumn(i);
+                        return;
                     }
                 }
-               transientState.setActiveColumn(null);
             }
+            transientState.setActiveColumn(null);
         });
 
         // Add focus/mouse/key listeners for supporting the respective
@@ -1578,18 +1551,12 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
             }
         });
 
-               // 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);
-            }
-        });
+        OpenStrategy os = new OpenStrategy(tree);
+        os.addPostSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+            //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
@@ -1611,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;
-
-    /**
-     * <code>true</code> 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.
@@ -2427,7 +2300,7 @@ class GraphExplorerImpl extends GraphExplorerImplBase implements Listener, Graph
 //                    System.out.println("MODCOUNT: " + modCount + " vs. " + count);
                     if (modCount != count)
                         return;
-                    widgetSelectionChanged(true);
+                    resetSelection();
                 }
             });
         }