001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.explorer.view;
043:
044: import java.awt.Dimension;
045: import java.awt.EventQueue;
046: import java.awt.event.ActionEvent;
047: import java.lang.reflect.InvocationTargetException;
048: import javax.swing.AbstractAction;
049: import javax.swing.Action;
050: import javax.swing.JFrame;
051: import javax.swing.JScrollBar;
052: import javax.swing.JScrollPane;
053: import org.netbeans.junit.NbTestCase;
054: import org.openide.explorer.ExplorerManager;
055: import org.openide.nodes.AbstractNode;
056: import org.openide.nodes.Children;
057: import org.openide.nodes.FilterNode;
058: import org.openide.nodes.Node;
059: import org.openide.util.HelpCtx;
060: import org.openide.util.Lookup;
061: import org.openide.util.RequestProcessor;
062: import org.openide.util.actions.NodeAction;
063: import org.openide.util.actions.SystemAction;
064:
065: /**
066: *
067: * @author Marian Petras, Andrei Badea
068: */
069: public final class TreeViewTest extends NbTestCase {
070:
071: private TestTreeView treeView;
072: private JFrame testWindow;
073: private volatile boolean isScrolledDown;
074: private final Object semaphore = new Object();
075:
076: public TreeViewTest(String testName) {
077: super (testName);
078: }
079:
080: /**
081: * Tests whether <code>JTree</code>'s property <code>scrollsOnExpand</code>
082: * is taken into account in
083: * <code>TreeView.TreePropertyListener.treeExpanded(...)</code>.
084: */
085: public void testAutoscrollOnOff() throws InterruptedException {
086: assert !EventQueue.isDispatchThread();
087:
088: testWindow = new ExplorerWindow();
089: testWindow.getContentPane().add(treeView = new TestTreeView());
090:
091: class WindowDisplayer implements Runnable {
092: public void run() {
093: testWindow.pack();
094: testWindow.setVisible(true);
095: }
096: }
097:
098: class Detector implements Runnable {
099: public void run() {
100: if (!EventQueue.isDispatchThread()) {
101: EventQueue.invokeLater(this );
102: return;
103: }
104:
105: isScrolledDown = !treeView.isUp();
106:
107: synchronized (semaphore) {
108: semaphore.notify();
109: }
110: }
111: }
112:
113: class Tester implements Runnable {
114: private final boolean autoscroll;
115: private final int part;
116:
117: Tester(boolean autoscroll, int part) {
118: this .autoscroll = autoscroll;
119: this .part = part;
120: }
121:
122: public void run() {
123: assert (part == 1) || (part == 2);
124: if (part == 1) {
125: treeView.collapse();
126: treeView.scrollUp();
127: assert treeView.isUp();
128: } else {
129: treeView.setAutoscroll(autoscroll);
130: treeView.expand(); //<-- posts a request to the RequestProcessor
131: RequestProcessor.getDefault().post(new Detector(),
132: 1000 /*ms*/);
133: }
134: }
135: }
136:
137: try {
138: EventQueue.invokeAndWait(new WindowDisplayer());
139: } catch (InvocationTargetException ex) {
140: ex.printStackTrace();
141: }
142:
143: //Wait for the AWT thread to actually display the dialog:
144: Thread.currentThread().sleep(5000);
145:
146: EventQueue.invokeLater(new Tester(true, 1));
147: Thread.currentThread().sleep(2000); //wait for update of the screen
148: EventQueue.invokeLater(new Tester(true, 2));
149: synchronized (semaphore) {
150: semaphore.wait();
151: }
152: assertTrue("Check the view has scrolled", isScrolledDown);
153:
154: EventQueue.invokeLater(new Tester(false, 1));
155: Thread.currentThread().sleep(2000); //wait for update of the screen
156: EventQueue.invokeLater(new Tester(false, 2));
157: synchronized (semaphore) {
158: semaphore.wait();
159: }
160: assertTrue("Check the view has not scrolled", !isScrolledDown);
161:
162: EventQueue.invokeLater(new Tester(true, 1)); //just collapse the tree
163: Thread.currentThread().sleep(2000);
164: }
165:
166: private static final class TestTreeView extends BeanTreeView {
167:
168: private final Node rootNode;
169: final JScrollBar verticalScrollBar;
170: private transient ExplorerManager explManager;
171:
172: TestTreeView() {
173: super ();
174: tree.setAutoscrolls(true);
175:
176: setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
177: verticalScrollBar = getVerticalScrollBar();
178:
179: rootNode = new AbstractNode(new TreeChildren());
180: rootNode.setDisplayName("Root node");
181:
182: tree.setRowHeight(20);
183:
184: Dimension prefSize = new Dimension(200, 6 * tree
185: .getRowHeight() + 8);
186: prefSize.width = (int) (prefSize.width * 1.25f)
187: + verticalScrollBar.getWidth();
188: setPreferredSize(prefSize);
189: }
190:
191: public void addNotify() {
192: super .addNotify();
193: explManager = ExplorerManager.find(this );
194: explManager.setRootContext(rootNode);
195: collapse();
196: }
197:
198: void setAutoscroll(boolean autoscroll) {
199: tree.setScrollsOnExpand(autoscroll);
200: }
201:
202: void scrollUp() {
203: verticalScrollBar.setValue(verticalScrollBar.getMinimum());
204: }
205:
206: boolean isUp() {
207: return verticalScrollBar.getValue() == verticalScrollBar
208: .getMinimum();
209: }
210:
211: void expand() {
212: tree.expandRow(4);
213: }
214:
215: void collapse() {
216: tree.collapseRow(4);
217: }
218:
219: final static class TreeChildren extends Children.Array {
220:
221: private static final char[] letters = new char[] { 'A',
222: 'B', 'C', 'D', 'E' };
223:
224: TreeChildren() {
225: this (-1);
226: }
227:
228: TreeChildren(final int first) {
229: super ();
230:
231: Node[] childNodes = new Node[5];
232: int i;
233: if (first == -1) {
234: for (i = 0; i < childNodes.length; i++) {
235: AbstractNode childNode = new AbstractNode(
236: new TreeChildren(i));
237: childNode.setDisplayName("Child node " + i);
238: childNodes[i] = childNode;
239: }
240: } else {
241: for (i = 0; i < childNodes.length; i++) {
242: AbstractNode childNode = new AbstractNode(
243: Children.LEAF);
244: StringBuffer buf = new StringBuffer(3);
245: childNode.setDisplayName(buf.append(first)
246: .append('.').append(letters[i])
247: .toString());
248: childNodes[i] = childNode;
249: }
250: }
251: add(childNodes);
252: }
253:
254: }
255:
256: }
257:
258: private static final class ExplorerWindow extends JFrame implements
259: ExplorerManager.Provider {
260:
261: private final ExplorerManager explManager = new ExplorerManager();
262:
263: ExplorerWindow() {
264: super ("TreeView test"); //NOI18N
265: }
266:
267: public ExplorerManager getExplorerManager() {
268: return explManager;
269: }
270:
271: }
272:
273: /**
274: * Used as the preferred actions by the nodes below
275: */
276: private static class MyAction extends NodeAction {
277:
278: public boolean enable(Node[] nodes) {
279: return true;
280: }
281:
282: public void performAction(Node[] nodes) {
283: }
284:
285: public HelpCtx getHelpCtx() {
286: return HelpCtx.DEFAULT_HELP;
287: }
288:
289: public String getName() {
290: return "My Action";
291: }
292:
293: public Action createContextAwareInstance(Lookup actionContext) {
294: return new MyDelegateAction(actionContext);
295: }
296: }
297:
298: /**
299: * Returned by MyAction.createContextAwareInstance().
300: */
301: private static class MyDelegateAction extends AbstractAction {
302: Lookup contextLookup;
303:
304: public MyDelegateAction(Lookup contextLookup) {
305: this .contextLookup = contextLookup;
306: }
307:
308: public void actionPerformed(ActionEvent e) {
309: }
310: }
311:
312: private static class NodeWhichHasItselfInLookup extends
313: AbstractNode {
314: public NodeWhichHasItselfInLookup() {
315: super (Children.LEAF);
316: }
317:
318: public Action getPreferredAction() {
319: return SystemAction.get(MyAction.class);
320: }
321: }
322:
323: private static class NodeWhichDoesntHaveItselfInLookup extends
324: AbstractNode {
325: public NodeWhichDoesntHaveItselfInLookup() {
326: super (Children.LEAF, Lookup.EMPTY);
327: }
328:
329: public Action getPreferredAction() {
330: return SystemAction.get(MyAction.class);
331: }
332: }
333:
334: /**
335: * Tests that the context lookup created by TreeView.takeAction() only contains
336: * the node once when the node contains itself in its lookup.
337: */
338: public void testTakeActionNodeInLookup() {
339: doTestTakeAction(new NodeWhichHasItselfInLookup());
340: }
341:
342: /**
343: * Tests that the context lookup created by TreeView.takeAction() only contains
344: * the node once when the node doesn't contain itself in its lookup.
345: */
346: public void testTakeActionNodeNotInLookup() {
347: doTestTakeAction(new NodeWhichDoesntHaveItselfInLookup());
348: }
349:
350: /**
351: * Tests that the context lookup created by TreeView.takeAction() only contains
352: * the node once when the node contains itself in its lookup and is filtered by a FilterNode.
353: */
354: public void testTakeActionNodeInLookupAndFiltered() {
355: doTestTakeAction(new FilterNode(
356: new NodeWhichHasItselfInLookup()));
357: }
358:
359: /**
360: * Tests that the context lookup created by TreeView.takeAction() only contains
361: * the node once when the node doesn't contain itself in its lookup
362: * and is filtered by a FilterNode.
363: */
364: public void testTakeActionNodeNotInLookupAndFiltered() {
365: doTestTakeAction(new FilterNode(
366: new NodeWhichDoesntHaveItselfInLookup()));
367: }
368:
369: private void doTestTakeAction(Node node) {
370: // if the preferred action instanceof ContextAwareAction
371: // calls its createContextAwareInstance() method
372: Action a = TreeView.takeAction(node.getPreferredAction(), node);
373: int count = ((MyDelegateAction) a).contextLookup.lookup(
374: new Lookup.Template(Node.class)).allInstances().size();
375: assertEquals(
376: "The context lookup created by TreeView.takeAction() should contain the node only once.",
377: 1, count);
378: }
379:
380: }
|