/* * $Id$ * * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javatest.exec; import java.awt.EventQueue; import java.util.Enumeration; import javax.swing.JTree; import javax.swing.ToolTipManager; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import com.sun.javatest.Parameters; import com.sun.javatest.TestFilter; import com.sun.javatest.TestResult; import com.sun.javatest.TestResultTable; import com.sun.javatest.tool.UIFactory; import com.sun.javatest.util.Debug; import com.sun.javatest.util.DynamicArray; import javax.swing.tree.TreeSelectionModel; /** * Tree to render test structure. */ class TestTree extends JTree { /** * @param uif The UI factory object to use. * @param model The GUI model object. * @param fConfig The active filter configuration object. * @param treeModel The data model. */ public TestTree(UIFactory uif, TreePanelModel model, FilterSelectionHandler fh, TestTreeModel treeModel) { super(); this.uif = uif; this.tpm = model; this.filterHandler = fh; filterHandler.addObserver(watcher); //currModel = new TestTreeModel(null); currModel = treeModel; setModel(currModel); setLargeModel(true); setScrollsOnExpand(true); setName("tree"); ToolTipManager.sharedInstance().registerComponent(this); } /** * Call when it is likely that the Parameters or filters have changed internally. */ public void updateGUI() { // delete this if the model becomes top-level // invalidate the node info cache because we don't know what part // of the parameters have changed. A filter change is likely if // the user has just completed an interview repaint(); } public void setModel(TreeModel newModel) { super.setModel(newModel); newModel.addTreeModelListener(watcher); } // ---------- private ---------- void setParameters(Parameters p) { // this is only here to notify the model when we change things which // affect the tree structure itself. // otherwise, most changes are the result of filter changes } /** * For proper operation, this method is recommended over the method on JTree. * * @param mod The new data model. */ void setTreeModel(TestTreeModel mod) { if (currModel != null) currModel.removeTreeModelListener(watcher); currModel = mod; setModel(currModel); } TestResultTable getTestResultTable() { // remove this method when the model becomes independent return currModel.getTestResultTable(); } TreePath[] snapshotSelectedPaths() { TreeSelectionModel tsm = getSelectionModel(); if (tsm == null) return null; return tsm.getSelectionPaths(); } void restoreSelection(String[] selectedUrls) { if (selectedUrls == null || selectedUrls.length == 0) return; if (currModel instanceof TestTreeModel) { final TreePath[] paths = currModel.urlsToPaths(selectedUrls); if (!EventQueue.isDispatchThread()) { EventQueue.invokeLater(new Runnable() { @Override public void run() { restoreSelection(paths); } }); } else restoreSelection(paths); } } private void restoreSelection(TreePath[] selectedPaths) { if (selectedPaths == null || selectedPaths.length == 0) return; TreeSelectionModel tsm = getSelectionModel(); if (tsm == null) return; // add or set ??? tsm.addSelectionPaths(selectedPaths); } /** * Record which tree nodes are currently visible. * @see #restorePaths */ TreePath[] snapshotOpenPaths() { // currModel.getRoot()==null may indicate that the GUI has been // disposed already (?). We have seen exceptions in the code // below because this situation occurs. if (currModel == null || currModel.getRoot() == null) return null; TreePath[] paths = new TreePath[0]; Enumeration e = getDescendantToggledPaths(new TreePath(currModel.getRoot())); while (e != null && e.hasMoreElements()) { TreePath tp = (e.nextElement()); if (!isVisible(tp)) // if we can't see it, we don't care continue; if (!isExpanded(tp)) // if it's not expanded, we don't need it continue; paths = DynamicArray.append(paths, tp); } // while return paths; } /** * Restore open paths in the tree. Should be called on the event thread. * @param openUrls The path urls to restore. * @param queue True if the request should be put into the event queue at the * end - this can be important for proper queuing of operations. */ void restorePaths(final String[] openUrls, boolean queue) { if (openUrls == null || openUrls.length == 0) return; if (currModel instanceof TestTreeModel) { final TreePath[] paths = currModel.urlsToPaths(openUrls); if (!EventQueue.isDispatchThread() || queue) { EventQueue.invokeLater(new Runnable() { public void run() { restorePaths(paths); } }); } else restorePaths(paths); } } /** * Attempt to restore paths which were previously recorded. * @see #snapshotOpenPaths */ void restorePaths(TreePath[] paths) { // make sure root still matches if (paths == null || paths.length == 0) return; for (int i = 0; i < paths.length; i++) { if (paths[i].getPathCount() == 1) continue; //expandPath(paths[i]); setExpandedState(paths[i], true); makeVisible(paths[i]); } // for } private UIFactory uif; private TestTreeModel currModel; private TreePanelModel tpm; private FilterSelectionHandler filterHandler; private EventWatcher watcher = new EventWatcher(); //protected static boolean debug = boolean.getboolean("debug." + testtree.class.getname()); private static int debug = Debug.getInt("debug." + TestTree.class.getName()); private class EventWatcher implements TreeModelListener, FilterSelectionHandler.Observer { // TreeModelListener - on event thread public void treeNodesChanged(TreeModelEvent e) { // NOTE: cannot get selected item from tree because if it has // been replaced already, there may be nothing selected String activeTest = tpm.getSelectedTest(); if (activeTest == null) { return; } Object[] targets = e.getChildren(); if (targets == null) return; // don't care then for (int i = 0; i < targets.length; i++) if (targets[i] instanceof TT_TestNode) { TestResult tr = ((TT_TestNode)targets[i]).getTestResult(); if (tr.getTestName().equals(activeTest)) tpm.showTest(tr); } repaint(); } public void treeNodesInserted(TreeModelEvent e) { // NOTE: cannot get selected item from tree because if it has // been replaced already, there may be nothing selected String activeTest = tpm.getSelectedTest(); if (activeTest == null) { return; } Object[] targets = e.getChildren(); if (targets == null) return; // don't care then for (int i = 0; i < targets.length; i++) if (targets[i] instanceof TT_TestNode) { TestResult tr = ((TT_TestNode)targets[i]).getTestResult(); if (tr.getTestName().equals(activeTest)) tpm.showTest(tr); } } public void treeNodesRemoved(TreeModelEvent e) { // XXX need to handle case when shown node is removed // select root in that case? } public void treeStructureChanged(TreeModelEvent e) { TreePath[] tp = snapshotOpenPaths(); TreePath[] selected = snapshotSelectedPaths(); //tpm.showNode(getModel().getRoot(), new TreePath(getModel().getRoot())); if (tp != null && tp.length != 0) restorePaths(tp); restoreSelection(selected); } // FilterConfig.Observer - may not be on event thread public void filterUpdated(TestFilter f) { updateGUI(); } public void filterSelected(TestFilter f) { updateGUI(); } public void filterAdded(TestFilter f) { // don't care } public void filterRemoved(TestFilter f) { // don't care } } }