This is an automated email from the ASF dual-hosted git repository.

ebakke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 0b03065  [NETBEANS-4857] Prevent property change events during tree 
sync
0b03065 is described below

commit 0b030658851117088ba280db9458f3799e7817bf
Author: Michael Küttner <cutter....@gmx.de>
AuthorDate: Mon Oct 12 22:53:13 2020 +0200

    [NETBEANS-4857] Prevent property change events during tree sync
    
    An ExplorerManager can be shared by multiple OutlineViews to synchronize 
displayed nodes and selection. This works fine for single node selection 
(ListSelectionModel.SINGLE_SELECTION).
    
    But in case of multiple selection 
(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) the ExplorerManager creates 
way too many PropertyChangeEvents.
    
    Use setValueAdjusting to coalesce these events.
---
 .../src/org/openide/explorer/view/OutlineView.java |   7 +
 .../view/OutlineViewSynchronizationTest.java       | 246 +++++++++++++++++++++
 2 files changed, 253 insertions(+)

diff --git 
a/platform/openide.explorer/src/org/openide/explorer/view/OutlineView.java 
b/platform/openide.explorer/src/org/openide/explorer/view/OutlineView.java
index 3cf63a5..eb71102 100644
--- a/platform/openide.explorer/src/org/openide/explorer/view/OutlineView.java
+++ b/platform/openide.explorer/src/org/openide/explorer/view/OutlineView.java
@@ -859,6 +859,10 @@ public class OutlineView extends JScrollPane {
         invalidate();
         validate();
         Node[] arr = manager.getSelectedNodes ();
+        
+        // [NETBEANS-4857]: prevent property change events during 
synchronization  
+        outline.getSelectionModel().setValueIsAdjusting(true);
+        
         outline.getSelectionModel().clearSelection();
         int size = outline.getRowCount();
         int firstSelection = -1;
@@ -876,6 +880,9 @@ public class OutlineView extends JScrollPane {
                 }
             }
         }
+        // [NETBEANS-4857]: re-activate property change events
+        outline.getSelectionModel().setValueIsAdjusting(false);
+        
 //        
System.err.println("\nOutlineView.synchronizeSelectedNodes("+java.util.Arrays.toString(arr)+"):
 "+
 //                           "columnModel = "+outline.getColumnModel()+", 
column selection model = "+outline.getColumnModel().getSelectionModel()+
 //                           ", column lead selection index = 
"+outline.getColumnModel().getSelectionModel().getLeadSelectionIndex()+"\n");
diff --git 
a/platform/openide.explorer/test/unit/src/org/openide/explorer/view/OutlineViewSynchronizationTest.java
 
b/platform/openide.explorer/test/unit/src/org/openide/explorer/view/OutlineViewSynchronizationTest.java
new file mode 100644
index 0000000..fc25047
--- /dev/null
+++ 
b/platform/openide.explorer/test/unit/src/org/openide/explorer/view/OutlineViewSynchronizationTest.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.openide.explorer.view;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.JPanel;
+import javax.swing.ListSelectionModel;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.swing.outline.Outline;
+import org.openide.explorer.ExplorerManager;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.ChildFactory;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.util.lookup.AbstractLookup;
+import org.openide.util.lookup.InstanceContent;
+
+/**
+ * Tests fix of Jira issue [NETBEANS-4857]. There should be no additional
+ * property change events during outline view tree synchronization.
+ *
+ * @author Michael Kuettner
+ */
+public final class OutlineViewSynchronizationTest extends NbTestCase {
+
+    public OutlineViewSynchronizationTest(String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected boolean runInEQ() {
+        return true;
+    }
+
+    public void testSingleOutlineSelection() throws InterruptedException, 
IllegalAccessException, InvocationTargetException, PropertyVetoException {
+
+        MultipleOutlinesPanel outlinesPanel = new MultipleOutlinesPanel(1);
+        outlinesPanel.addNotify();
+
+        Node[] nodes = 
outlinesPanel.getExplorerManager().getRootContext().getChildren().getNodes();
+
+        LoggingPropertyChangeListener evtLog = new 
LoggingPropertyChangeListener();
+        ExplorerManager manager = outlinesPanel.getExplorerManager();
+        manager.addPropertyChangeListener(evtLog);
+
+        // select a single node
+        manager.setSelectedNodes(new Node[]{nodes[0]});
+
+        assertEquals(1, evtLog.getEventCount());
+        assertEquals("[] -> [0]", evtLog.getEvent(0));
+        assertEquals(1, outlinesPanel.getOutline(0).getSelectedRowCount());
+
+        // remove already collected events
+        evtLog.clearEvents();
+
+        // select all nodes
+        manager.setSelectedNodes(nodes);
+
+        assertEquals(1, evtLog.getEventCount());
+        assertEquals("[0] -> [0,1,2,3,4,5,6,7,8,9]", evtLog.getEvent(0));
+        assertEquals(10, outlinesPanel.getOutline(0).getSelectedRowCount());
+    }
+
+    public void testMultipleOutlinesSelectionSynchronization() throws 
InterruptedException, IllegalAccessException, InvocationTargetException, 
PropertyVetoException {
+
+        MultipleOutlinesPanel outlinesPanel = new MultipleOutlinesPanel(4);
+        outlinesPanel.addNotify();
+
+        Node[] nodes = 
outlinesPanel.getExplorerManager().getRootContext().getChildren().getNodes();
+
+        LoggingPropertyChangeListener evtLog = new 
LoggingPropertyChangeListener();
+        ExplorerManager manager = outlinesPanel.getExplorerManager();
+        manager.addPropertyChangeListener(evtLog);
+
+        // select a single node
+        manager.setSelectedNodes(new Node[]{nodes[0]});
+
+        assertEquals(1, evtLog.getEventCount());
+        assertEquals("[] -> [0]", evtLog.getEvent(0));
+        assertEquals(1, outlinesPanel.getOutline(0).getSelectedRowCount());
+        assertEquals(1, outlinesPanel.getOutline(1).getSelectedRowCount());
+        assertEquals(1, outlinesPanel.getOutline(2).getSelectedRowCount());
+        assertEquals(1, outlinesPanel.getOutline(3).getSelectedRowCount());
+
+        // remove already collected events
+        evtLog.clearEvents();
+
+        // select all nodes
+        manager.setSelectedNodes(nodes);
+
+        assertEquals(1, evtLog.getEventCount());
+        assertEquals("[0] -> [0,1,2,3,4,5,6,7,8,9]", evtLog.getEvent(0));
+        assertEquals(10, outlinesPanel.getOutline(0).getSelectedRowCount());
+        assertEquals(10, outlinesPanel.getOutline(1).getSelectedRowCount());
+        assertEquals(10, outlinesPanel.getOutline(2).getSelectedRowCount());
+        assertEquals(10, outlinesPanel.getOutline(3).getSelectedRowCount());
+    }
+
+    /**
+     * A panel that provides an {@link ExplorerManager} and contains multiple
+     * panels with outlines.
+     */
+    public static class MultipleOutlinesPanel extends JPanel implements 
ExplorerManager.Provider {
+
+        private final ExplorerManager manager = new ExplorerManager();
+
+        public MultipleOutlinesPanel(int numberOfOutlineViews) {
+            setLayout(new FlowLayout(FlowLayout.LEFT));
+            for (int i = 0; i < numberOfOutlineViews; i++) {
+                add(new OutlinePanel());
+            }
+            Node rootNode = new AbstractNode(Children.create(new 
MultipleTreesNodeFactory(), false));
+            manager.setRootContext(rootNode);
+        }
+
+        public Outline getOutline(int index) {
+            OutlinePanel panel = (OutlinePanel) getComponent(index);
+            return panel.getOutlineView().getOutline();
+        }
+
+        @Override
+        public ExplorerManager getExplorerManager() {
+            return manager;
+        }
+    }
+
+    /**
+     * A panel that contains a single {@link OutlineView}.
+     */
+    private static class OutlinePanel extends JPanel {
+
+        private OutlineView outlineView;
+
+        public OutlinePanel() {
+            setLayout(new BorderLayout());
+
+            outlineView = new OutlineView("tree");
+            outlineView.getOutline().setRootVisible(false);
+            // activate multiple interval selection
+            
outlineView.getOutline().getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+            
outlineView.getOutline().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+
+            add(outlineView, BorderLayout.CENTER);
+        }
+
+        public OutlineView getOutlineView() {
+            return outlineView;
+        }
+    }
+
+    /**
+     * A simple node factory that creates some String nodes.
+     */
+    private static class MultipleTreesNodeFactory extends ChildFactory<String> 
{
+
+        @Override
+        protected boolean createKeys(final List<String> values) {
+            for (int i = 0; i < 10; i++) {
+                values.add(Integer.toString(i));
+            }
+            return true;
+        }
+
+        @Override
+        protected Node createNodeForKey(final String key) {
+            return new MultipeTreesNode(key);
+        }
+    }
+
+    /**
+     * A simple node for a String.
+     */
+    private static class MultipeTreesNode extends AbstractNode {
+
+        public MultipeTreesNode(final String value) {
+            super(Children.LEAF, new AbstractLookup(new InstanceContent()));
+            setName(value);
+        }
+    }
+
+    /**
+     * PropertyChangeListener implementation that keeps a string representation
+     * of each ExplorerManager.PROP_SELECTED_NODES event.
+     */
+    private static class LoggingPropertyChangeListener implements 
PropertyChangeListener {
+
+        private List<String> events = new ArrayList<String>();
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            if 
(ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
+                Node[] oldNodes = (Node[]) evt.getOldValue();
+                Node[] newNodes = (Node[]) evt.getNewValue();
+                events.add(nodesToString(oldNodes) + " -> " + 
nodesToString(newNodes));
+            }
+        }
+
+        private String nodesToString(final Node[] nodes) {
+            StringBuilder builder = new StringBuilder();
+            builder.append("[");
+            for (int i = 0; i < nodes.length; i++) {
+                builder.append(nodes[i].getName());
+                if (i != nodes.length - 1) {
+                    builder.append(",");
+                }
+            }
+            builder.append("]");
+            return builder.toString();
+        }
+
+        public int getEventCount() {
+            return events.size();
+        }
+
+        public String getEvent(int index) {
+            return events.get(index);
+        }
+
+        public void clearEvents() {
+            events.clear();
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to