]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerImpl.java
Revert "Fixed GraphExplorerImpl to set selection upon keyboard-based selection"
[simantics/platform.git] / bundles / org.simantics.browsing.ui.swt / src / org / simantics / browsing / ui / swt / GraphExplorerImpl.java
index 0e45f86ca4843fe53233ecec6217a07026ee4954..0168ad864ae5e98258498d76fef7f04f42ee4282 100644 (file)
@@ -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.
-     * 
-     * <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;
 
     /**
@@ -326,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.
@@ -349,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
@@ -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<NodeContext, Boolean>     autoExpanded  = new WeakHashMap<NodeContext, Boolean>();
+        Map<NodeContext, Boolean>     autoExpanded  = new WeakHashMap<>();
 
         
         @Override
@@ -480,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;
 
@@ -501,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;
 
@@ -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<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;
@@ -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<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)}
@@ -647,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
@@ -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<NodeContext> currentlyModifiedNodes   = new THashSet<NodeContext>();
+    private Set<NodeContext> 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<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
@@ -1576,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
@@ -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;
-
-    /**
-     * <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.
@@ -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<Map<Column, Object>> callback) {
         // Attempt to keep previous column widths.
-        Map<String, Integer> prevWidths = new HashMap<String, Integer>();
+        Map<String, Integer> 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<String, Integer> keyToIndex = new HashMap<String, Integer>();
+        HashMap<String, Integer> 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<Column, Object> map = new HashMap<Column, Object>();
+        Map<Column, Object> 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();
-            }
         });
     }