diff -r 067c411a9044 openide.explorer/apichanges.xml --- a/openide.explorer/apichanges.xml Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/apichanges.xml Sat Apr 17 21:38:39 2010 +0200 @@ -47,6 +47,21 @@ Explorer API + + + ListView can now display ".." item + + + + + + To simplify navigation "up" in the ListView one + can turn on showParentNode + property. + + + + OutlineView.setPropertyColumns, diff -r 067c411a9044 openide.explorer/manifest.mf --- a/openide.explorer/manifest.mf Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/manifest.mf Sat Apr 17 21:38:39 2010 +0200 @@ -2,5 +2,5 @@ OpenIDE-Module: org.openide.explorer OpenIDE-Module-Localizing-Bundle: org/openide/explorer/Bundle.properties AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 6.27 +OpenIDE-Module-Specification-Version: 6.28 diff -r 067c411a9044 openide.explorer/src/org/openide/explorer/view/Bundle.properties --- a/openide.explorer/src/org/openide/explorer/view/Bundle.properties Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/src/org/openide/explorer/view/Bundle.properties Sat Apr 17 21:38:39 2010 +0200 @@ -53,6 +53,8 @@ VetoSelectedNodes=Selected Nodes LBL_QUICKSEARCH=Search: +LBL_UP=.. + # TreeViewCellEditor # {0} - old name # {1} - new name diff -r 067c411a9044 openide.explorer/src/org/openide/explorer/view/ListView.java --- a/openide.explorer/src/org/openide/explorer/view/ListView.java Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/src/org/openide/explorer/view/ListView.java Sat Apr 17 21:38:39 2010 +0200 @@ -101,6 +101,7 @@ import org.openide.nodes.Node.Property; import org.openide.nodes.NodeOp; import org.openide.util.ContextAwareAction; +import org.openide.util.Exceptions; import org.openide.util.Mutex; import org.openide.util.NbBundle; import org.openide.util.Utilities; @@ -181,6 +182,9 @@ /** if true, the hierarchy traversal is allowed, if false, it is disabled */ private boolean traversalAllowed = true; + /** show parent node */ + private boolean showParentNode; + /** action preformer */ private ActionListener defaultProcessor; @@ -299,6 +303,26 @@ traversalAllowed = value; } + /** Is parent node (e.g. explored context shown)? + * @return true or false. Default is false. + * @since 6.28 + */ + public boolean isShowParentNode() { + return showParentNode; + } + + /** Shall the first node in the list be ".." representing currently + * explored context? By default it is not, but if you want to simplify + * the navigation in the {@link Node} hierarchy, you can turn this + * property on. + * + * @param show true to show the "..", false to not to do so + * @since 6.28 + */ + public void setShowParentNode(boolean show) { + showParentNode = show; + } + /** Get the current processor for default actions. * If not null, double-clicks or pressing Enter on * items in the view will not perform the default action on the selected node; rather the processor @@ -557,6 +581,11 @@ // Working methods // + final void setNode(Node n) { + boolean show = showParentNode && n != manager.getRootContext(); + model.setNode(n, show); + } + /* Initilizes the view. */ @Override @@ -577,7 +606,7 @@ manager.addVetoableChangeListener(wlvc = WeakListeners.vetoableChange(managerListener, manager)); manager.addPropertyChangeListener(wlpc = WeakListeners.propertyChange(managerListener, manager)); - model.setNode(manager.getExploredContext()); + setNode(manager.getExploredContext()); updateSelection(); } else { @@ -595,7 +624,7 @@ // bugfix #23974, model doesn't reflect an explorer context change // because any listener was not active - model.setNode(manager.getExploredContext()); + setNode(manager.getExploredContext()); list.addMouseListener(popupSupport); } } @@ -662,6 +691,15 @@ return; } + if (showParentNode && NodeListModel.findVisualizerDepth(model, v) == -1) { + try { + manager.setExploredContextAndSelection(node.getParentNode(), new Node[] { node }); + } catch (PropertyVetoException ex) { + // OK, let it be + } + return; + } + // on double click - invoke default action, if there is any // (unless user holds CTRL key what means that we should always dive into the context) Action a = node.getPreferredAction(); @@ -1393,7 +1431,7 @@ } if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) { - model.setNode(manager.getExploredContext()); + setNode(manager.getExploredContext()); //System.out.println("Children: " + java.util.Arrays.asList (list.getValues ())); // NOI18N return; diff -r 067c411a9044 openide.explorer/src/org/openide/explorer/view/NodeListModel.java --- a/openide.explorer/src/org/openide/explorer/view/NodeListModel.java Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/src/org/openide/explorer/view/NodeListModel.java Sat Apr 17 21:38:39 2010 +0200 @@ -66,6 +66,9 @@ /** parent node */ private transient VisualizerNode parent; + /** should parent node be visible? */ + private transient boolean showParent; + /** originally selected item */ private transient Object selectedObject; @@ -100,7 +103,11 @@ /** Changes the root of the model. This is thread safe method. * @param root the root of the model */ - public void setNode(final Node root) { + public void setNode(Node root) { + setNode(root, false); + } + + final void setNode(final Node root, final boolean showRoot) { Mutex.EVENT.readAccess(new Runnable() { @Override public void run() { @@ -110,7 +117,7 @@ } VisualizerNode v = VisualizerNode.getVisualizer(null, root); - if (v == parent) { + if (v == parent && showParent == showRoot) { // no change return; } @@ -118,6 +125,7 @@ removeAll(); parent.removeNodeModel(listener()); + showParent = showRoot; parent = v; selectedObject = v; clearChildrenCount(); @@ -172,7 +180,7 @@ */ @Override public int getSize() { - int s = findSize(parent, -1, depth); + int s = findSize(parent, showParent, -1, depth); return s; } @@ -180,7 +188,7 @@ */ @Override public Object getElementAt(int i) { - return findElementAt(parent, i, -1, depth); + return findElementAt(parent, showParent, i, -1, depth); } /** Finds index of given object. @@ -223,12 +231,16 @@ * @param depth the depth to scan * @return number of children */ - private int findSize(VisualizerNode vis, int index, int depth) { + private int findSize(VisualizerNode vis, boolean includeOwnself, int index, int depth) { Info info = childrenCount.get(vis); if (info != null) { return info.childrenCount; } + if (includeOwnself) { + index++; + } + // only my children int tmp = 0; @@ -247,11 +259,11 @@ // count node v tmp++; // now count children of node v - tmp += findSize(v, index + tmp, depth); + tmp += findSize(v, false, index + tmp, depth); } } - info.childrenCount = tmp; + info.childrenCount = includeOwnself ? tmp + 1 : tmp; childrenCount.put(vis, info); return tmp; } @@ -263,7 +275,15 @@ * @param depth the depth to scan * @return the children */ - private VisualizerNode findElementAt(VisualizerNode vis, int indx, int realIndx, int depth) { + private VisualizerNode findElementAt(VisualizerNode vis, boolean countSelf, int indx, int realIndx, int depth) { + if (countSelf) { + if (indx == 0) { + return vis; + } else { + indx--; + } + } + if (--depth == 0) { // last depth is handled in special way return (VisualizerNode) vis.getChildAt(indx); @@ -277,11 +297,11 @@ return v; } - int s = findSize(v, ++realIndx, depth); + int s = findSize(v, false, ++realIndx, depth); if (indx < s) { // search this child - return findElementAt(v, indx, realIndx, depth); + return findElementAt(v, false, indx, realIndx, depth); } // go to next child diff -r 067c411a9044 openide.explorer/src/org/openide/explorer/view/NodeRenderer.java --- a/openide.explorer/src/org/openide/explorer/view/NodeRenderer.java Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/src/org/openide/explorer/view/NodeRenderer.java Sat Apr 17 21:38:39 2010 +0200 @@ -50,6 +50,7 @@ import javax.swing.*; import javax.swing.tree.TreeCellRenderer; +import org.openide.util.NbBundle; /** Default renderer for nodes. Can paint either Nodes directly or @@ -158,8 +159,13 @@ } String text = vis.getHtmlDisplayName(); + if (list.getModel() instanceof NodeListModel) { + int depth = NodeListModel.findVisualizerDepth(list.getModel(), vis); + if (depth == -1) { + text = NbBundle.getMessage(NodeRenderer.class, "LBL_UP"); + } + } boolean isHtml = text != null; - if (!isHtml) { text = vis.getDisplayName(); } diff -r 067c411a9044 openide.explorer/test/unit/src/org/openide/explorer/view/ListViewWithUpTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/ListViewWithUpTest.java Sat Apr 17 21:38:39 2010 +0200 @@ -0,0 +1,193 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.openide.explorer.view; + +import java.awt.EventQueue; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import javax.swing.JFrame; +import org.netbeans.junit.NbTestCase; +import org.openide.explorer.ExplorerManager; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; + +/** + * A test that verifies few aspects of the ListView implementation. + * + * @author Petr Nejedly + */ +public final class ListViewWithUpTest extends NbTestCase { + + private ListView view; + private ExplorerWindow testWindow; + + public ListViewWithUpTest(String testName) { + super(testName); + } + + /** + * Tests whether the ListView doesn't try to scroll to a changed node + * on a change event. See issue 88209 + */ + public void testNoScrollOnIconChange() throws Exception { + assert !EventQueue.isDispatchThread(); + + testWindow = new ExplorerWindow(); + testWindow.getContentPane().add(view = new ListView()); + view.setShowParentNode(true); + + Ch20 ch = new Ch20(""); + Node root = new AbstractNode(ch); + root.setName("Root"); + + testWindow.getExplorerManager().setRootContext(root); + awtRequest(new Callable() { + @Override + public Void call() { + testWindow.pack(); + testWindow.setVisible(true); + return null; + } + }); + + while (!awtRequest(new Callable() { + @Override + public Boolean call() throws Exception { + return testWindow.isShowing(); + } + })) { + Thread.sleep(100); + } + + assertEquals("Just 20 items for root", 20, view.model.getSize()); + testWindow.getExplorerManager().setExploredContext(root.getChildren().getNodeAt(10)); + awtRequest(new Callable() { + @Override + public Void call() { + return null; + } + }); + assertEquals("21 items for non-root", 21, view.model.getSize()); + awtRequest(new Callable() { + @Override + public Void call() { + view.performObjectAt(0, 0); + return null; + } + }); + assertEquals("Moved back to root", root, testWindow.getExplorerManager().getExploredContext()); + + // cleanup + awtRequest(new Callable() { + @Override + public Void call() { + testWindow.setVisible(false); + return null; + } + }); + } + + private static final class Ch20 extends Children.Keys { + private final String prefix; + + public Ch20(String prefix) { + this.prefix = prefix; + } + + + @Override + protected void addNotify() { + List arr = new ArrayList(); + for (int i = 0; i < 20; i++) { + arr.add(i); + } + setKeys(arr); + } + + + @Override + protected Node[] createNodes(Integer key) { + AbstractNode an = new AbstractNode(new Ch20(prefix + key)); + an.setName(prefix + key); + return new Node[] { an }; + } + + } + + + private static final class ExplorerWindow extends JFrame + implements ExplorerManager.Provider { + + private final ExplorerManager explManager = new ExplorerManager(); + + ExplorerWindow() { + super("ListView test"); //NOI18N + } + + @Override + public ExplorerManager getExplorerManager() { + return explManager; + } + } + + private static T awtRequest(final Callable call) throws Exception { + final AtomicReference value = new AtomicReference(); + final Exception[] exc = new Exception[1]; + EventQueue.invokeAndWait(new Runnable() { + @Override + public void run() { + try { + value.set(call.call()); + } catch (Exception ex) { + exc[0] = ex; + } + } + }); + + if (exc[0] != null) throw exc[0]; + return value.get(); + } + +} diff -r 067c411a9044 openide.explorer/test/unit/src/org/openide/explorer/view/NodeListModelTest.java --- a/openide.explorer/test/unit/src/org/openide/explorer/view/NodeListModelTest.java Sat Apr 17 09:10:37 2010 +0200 +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/NodeListModelTest.java Sat Apr 17 21:38:39 2010 +0200 @@ -44,6 +44,7 @@ import java.lang.ref.WeakReference; import java.util.LinkedList; import java.util.List; +import javax.swing.tree.TreeNode; import org.netbeans.junit.NbTestCase; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -60,6 +61,7 @@ super(name); } + @Override protected boolean runInEQ() { return true; } @@ -114,6 +116,24 @@ // childrenCount model.getSize(); } + + public void testIsRootIncluded() { + Node c = new AbstractNode(new CNodeChildren()); + NodeListModel model = new NodeListModel(); + model.setNode(c, true); + + assertEquals(NO_OF_NODES + 1, model.getSize()); + + assertNode("Parent is first", c, model.getElementAt(0)); + for (int i= 0; i < NO_OF_NODES; i++) { + assertNode(i + "th node", c.getChildren().getNodeAt(i), model.getElementAt(i + 1)); + } + } + + private static void assertNode(String msg, Node n, Object e) { + TreeNode v = Visualizer.findVisualizer(n); + assertEquals(msg, v, e); + } /* * Children for testNodesAreReferenced. @@ -122,12 +142,13 @@ public CNodeChildren() { List myKeys = new LinkedList(); for (int i = 0; i < NO_OF_NODES; i++) { - myKeys.add(new Integer(i)); + myKeys.add(Integer.valueOf(i)); } setKeys(myKeys); } + @Override protected Node[] createNodes(Object key) { AbstractNode an = new AbstractNode(Children.LEAF); an.setName(key.toString());