Source Code Cross Referenced for JTreeSortNavigator.java in  » IDE » DrJava » edu » rice » cs » util » docnavigation » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE » DrJava » edu.rice.cs.util.docnavigation 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*BEGIN_COPYRIGHT_BLOCK
0002:         *
0003:         * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
0004:         * All rights reserved.
0005:         * 
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions are met:
0008:         *    * Redistributions of source code must retain the above copyright
0009:         *      notice, this list of conditions and the following disclaimer.
0010:         *    * Redistributions in binary form must reproduce the above copyright
0011:         *      notice, this list of conditions and the following disclaimer in the
0012:         *      documentation and/or other materials provided with the distribution.
0013:         *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
0014:         *      names of its contributors may be used to endorse or promote products
0015:         *      derived from this software without specific prior written permission.
0016:         * 
0017:         * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0018:         * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0019:         * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0020:         * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0021:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0022:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0023:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0024:         * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0025:         * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0026:         * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0027:         * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0028:         *
0029:         * This software is Open Source Initiative approved Open Source Software.
0030:         * Open Source Initative Approved is a trademark of the Open Source Initiative.
0031:         * 
0032:         * This file is part of DrJava.  Download the current version of this project
0033:         * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
0034:         * 
0035:         * END_COPYRIGHT_BLOCK*/
0036:
0037:        package edu.rice.cs.util.docnavigation;
0038:
0039:        import javax.swing.*;
0040:        import javax.swing.event.TreeSelectionListener;
0041:        import javax.swing.event.TreeSelectionEvent;
0042:        import javax.swing.event.TreeExpansionListener;
0043:        import javax.swing.event.TreeExpansionEvent;
0044:        import javax.swing.tree.*;
0045:        import java.io.File;
0046:        import java.awt.*;
0047:        import java.util.*;
0048:        import java.awt.dnd.*;
0049:        import java.awt.datatransfer.*;
0050:        import java.io.File;
0051:        import edu.rice.cs.util.*;
0052:        import edu.rice.cs.util.swing.*;
0053:        import edu.rice.cs.drjava.DrJavaRoot;
0054:
0055:        public class JTreeSortNavigator<ItemT extends INavigatorItem> extends
0056:                JTree implements  IDocumentNavigator<ItemT>,
0057:                TreeSelectionListener, TreeExpansionListener,
0058:                DropTargetListener {
0059:
0060:            /** The model of the tree. */
0061:            private final DefaultTreeModel _model;
0062:
0063:            /** The currently selected item.  Updated by a listener. It is not volatile because all accessed are protected by 
0064:             *  explicit synchronization.
0065:             */
0066:            private volatile NodeData<ItemT> _current;
0067:
0068:            /** Maps documents to tree nodes. */
0069:            private final HashMap<ItemT, LeafNode<ItemT>> _doc2node = new HashMap<ItemT, LeafNode<ItemT>>();
0070:
0071:            /** Maps path's to nodes and nodes to paths. */
0072:            private final BidirectionalHashMap<String, InnerNode<?, ItemT>> _path2node = new BidirectionalHashMap<String, InnerNode<?, ItemT>>();
0073:
0074:            /** The collection of INavigationListeners listening to this JListNavigator */
0075:            private final Vector<INavigationListener<? super  ItemT>> navListeners = new Vector<INavigationListener<? super  ItemT>>();
0076:
0077:            /** The renderer for this JTree. */
0078:            private final CustomTreeCellRenderer _renderer;
0079:
0080:            private volatile DisplayManager<? super  ItemT> _displayManager;
0081:            private volatile Icon _rootIcon;
0082:
0083:            private java.util.List<GroupNode<ItemT>> _roots = new LinkedList<GroupNode<ItemT>>();
0084:
0085:            /** Sets the foreground color of this JTree
0086:             * @param c the color to set to
0087:             */
0088:            public void setForeground(Color c) {
0089:                super .setForeground(c);
0090:                if (_renderer != null)
0091:                    _renderer.setTextNonSelectionColor(c);
0092:            }
0093:
0094:            /** Sets the background color of this tree
0095:             * @param c the color for the background 
0096:             */
0097:            public void setBackground(Color c) {
0098:                super .setBackground(c);
0099:                if (_renderer != null)
0100:                    _renderer.setBackgroundNonSelectionColor(c);
0101:            }
0102:
0103:            /** Standard constructor.
0104:             * @param projRoot the path identifying the root node for the project
0105:             */
0106:            public JTreeSortNavigator(String projRoot) {
0107:
0108:                super (new DefaultTreeModel(new RootNode<ItemT>(projRoot
0109:                        .substring(projRoot.lastIndexOf(File.separator) + 1))));
0110:
0111:                addTreeSelectionListener(this );
0112:                addTreeExpansionListener(this );
0113:
0114:                _model = (DefaultTreeModel) getModel();
0115:                _renderer = new CustomTreeCellRenderer();
0116:                _renderer.setOpaque(false);
0117:                setCellRenderer(_renderer);
0118:                //    getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
0119:                getSelectionModel().setSelectionMode(
0120:                        TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
0121:                setRowHeight(18);
0122:                //    System.err.println(isEditable());
0123:            }
0124:
0125:            /** Alternate constructor specifying the display manager that provides icons for the navigator.
0126:             * @param projRoot the path identifying the root node for the project
0127:             * @param dm the display manager for the navigagtor
0128:             */
0129:            public JTreeSortNavigator(String projRoot,
0130:                    DisplayManager<? super  ItemT> dm) {
0131:                this (projRoot);
0132:                _displayManager = dm;
0133:            }
0134:
0135:            /** Sets the display manager that is used to select icons for the leaves of the tree.
0136:             *  This does not apply to the inner nodes or the root.
0137:             */
0138:            public void setDisplayManager(DisplayManager<? super  ItemT> manager) {
0139:                _displayManager = manager;
0140:            }
0141:
0142:            /** Sets the icon to be displayed at the root of the tree */
0143:            public void setRootIcon(Icon ico) {
0144:                _rootIcon = ico;
0145:            }
0146:
0147:            /** @return an AWT component which interacts with this document navigator */
0148:            public Container asContainer() {
0149:                return this ;
0150:            }
0151:
0152:            /** Adds an <code>IDocument</code> to this navigator. Should only executed from event thread.
0153:             *  @param doc the document to be added into this navigator.
0154:             */
0155:            public void addDocument(ItemT doc) {
0156:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0157:                addDocument(doc, "");
0158:            }
0159:
0160:            /** Adds an <code>INavigatorItem</code> into this navigator in the position specified by path. 
0161:             * The actual behavior of the navigator and the position associated with a path are left up 
0162:             * to the implementing class.  Only runs in event-handling thread.
0163:             * @param doc the document to be added into this navigator.
0164:             * @param path in navigator to parent directory for doc
0165:             * @throws IllegalArgumentException if this navigator does not contain <code>relativeto</code> as tested by the
0166:             *         <code>contains</code> method.
0167:             */
0168:            public void addDocument(ItemT doc, String path) {
0169:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0170:                synchronized (_model) { // lock for mutation
0171:
0172:                    /* Identify root matching doc if any */
0173:                    GroupNode<ItemT> root = null;
0174:
0175:                    for (GroupNode<ItemT> r : _roots) {
0176:                        if (r.getFilter().accept(doc)) {
0177:                            root = r;
0178:                            break;
0179:                        }
0180:                    }
0181:
0182:                    if (root == null)
0183:                        return;
0184:
0185:                    /* Embed path in matching root, creating folder nodes if necessary */
0186:                    StringTokenizer tok = new StringTokenizer(path,
0187:                            File.separator);
0188:                    //ArrayList<String> elements = new ArrayList<String>();
0189:                    final StringBuilder pathSoFarBuf = new StringBuilder();
0190:                    InnerNode<?, ItemT> lastNode = root;
0191:                    while (tok.hasMoreTokens()) {
0192:                        String element = tok.nextToken();
0193:                        pathSoFarBuf.append(element).append('/');
0194:                        String pathSoFar = pathSoFarBuf.toString();
0195:                        InnerNode<?, ItemT> this Node;
0196:                        //System.out.println("pathsofar = " + pathSoFar);
0197:                        // if the node is not in the hashmap yet
0198:                        if (!_path2node.containsKey(pathSoFar)) {
0199:                            // make a new node
0200:
0201:                            /* this inserts a folder node */
0202:                            this Node = new FileNode<ItemT>(new File(pathSoFar));
0203:                            insertFolderSortedInto(this Node, lastNode);
0204:                            this .expandPath(new TreePath(lastNode.getPath()));
0205:                            // associate the path so far with that node
0206:                            _path2node.put(pathSoFar, this Node);
0207:                        } else {
0208:                            // System.out.println("path2node contains pathSoFar");
0209:                            this Node = _path2node.getValue(pathSoFar);
0210:                        }
0211:
0212:                        lastNode = this Node;
0213:
0214:                        //elements.add(element);
0215:                    }
0216:
0217:                    /* lastNode is the node of the folder to add into */
0218:
0219:                    LeafNode<ItemT> child = new LeafNode<ItemT>(doc);
0220:                    _doc2node.put(doc, child);
0221:                    insertNodeSortedInto(child, lastNode);
0222:                    //      _hasNonProjFilesOpen = (lastNode == root); 
0223:                    //    _model.insertNodeInto(child, lastNode, lastNode.getChildCount());
0224:                    this .expandPath(new TreePath(lastNode.getPath()));
0225:                }
0226:            }
0227:
0228:            private void addTopLevelGroupToRoot(InnerNode<?, ItemT> parent) {
0229:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0230:                synchronized (_model) { // lock for mutation
0231:                    int indexInRoots = _roots.indexOf(parent);
0232:                    int num = _model.getChildCount(_model.getRoot());
0233:                    int i;
0234:                    for (i = 0; i < num; i++) {
0235:                        TreeNode n = (TreeNode) _model.getChild(_model
0236:                                .getRoot(), i);
0237:                        if (_roots.indexOf(n) > indexInRoots)
0238:                            break;
0239:                    }
0240:                    _model.insertNodeInto(parent, (MutableTreeNode) _model
0241:                            .getRoot(), i);
0242:                }
0243:            }
0244:
0245:            /** Inserts the child node (INavigatorItem) into the sorted position as a parent node's child.  Only
0246:             *  executes in the event thread.  Assumes that _model lock is already held.
0247:             *  @param child the node to add
0248:             *  @param parent the node to add under
0249:             */
0250:            private void insertNodeSortedInto(LeafNode<ItemT> child,
0251:                    InnerNode<?, ItemT> parent) {
0252:                int numChildren = parent.getChildCount();
0253:                String newName = child.toString();
0254:                String oldName = parent.getUserObject().toString();
0255:                DefaultMutableTreeNode parentsKid;
0256:
0257:                /** Make sure that if the parent is a top level group, it is added to the tree model group. */
0258:                if (((DefaultMutableTreeNode) _model.getRoot())
0259:                        .getIndex(parent) == -1
0260:                        && _roots.contains(parent)) {
0261:                    addTopLevelGroupToRoot(parent);
0262:                }
0263:                int i;
0264:                for (i = 0; i < numChildren; i++) {
0265:                    parentsKid = ((DefaultMutableTreeNode) parent.getChildAt(i));
0266:                    if (parentsKid instanceof  InnerNode) {
0267:                        // do nothing, it's a folder
0268:                    } else if (parentsKid instanceof  LeafNode) {
0269:                        oldName = ((LeafNode<?>) parentsKid).getData()
0270:                                .getName();
0271:                        if ((newName.toUpperCase().compareTo(
0272:                                oldName.toUpperCase()) < 0))
0273:                            break;
0274:                    } else
0275:                        throw new IllegalStateException(
0276:                                "found a node in navigator that is not an InnerNode or LeafNode");
0277:                }
0278:                _model.insertNodeInto(child, parent, i);
0279:            }
0280:
0281:            /** Inserts a folder (String) into sorted position under the parent.  Only executes in event thread. Assumes that
0282:             * _model lock is already held
0283:             * @param child the folder to add
0284:             * @param parent the folder to add under
0285:             */
0286:            private void insertFolderSortedInto(InnerNode<?, ItemT> child,
0287:                    InnerNode<?, ItemT> parent) {
0288:                int numChildren = parent.getChildCount();
0289:                String newName = child.toString();
0290:                String oldName = parent.getUserObject().toString();
0291:                DefaultMutableTreeNode parentsKid;
0292:
0293:                if (((DefaultMutableTreeNode) _model.getRoot())
0294:                        .getIndex(parent) == -1
0295:                        && _roots.contains(parent)) {
0296:                    addTopLevelGroupToRoot(parent);
0297:                }
0298:
0299:                int countFolders = 0;
0300:                int i;
0301:                for (i = 0; i < numChildren; i++) {
0302:                    parentsKid = ((DefaultMutableTreeNode) parent.getChildAt(i));
0303:                    if (parentsKid instanceof  InnerNode) {
0304:                        countFolders++;
0305:                        oldName = parentsKid.toString();
0306:                        if ((newName.toUpperCase().compareTo(
0307:                                oldName.toUpperCase()) < 0))
0308:                            break;
0309:                    } else if (parentsKid instanceof  LeafNode)
0310:                        break;
0311:                    // we're out of folders, and starting into the files, so just break out.
0312:                    else
0313:                        throw new IllegalStateException(
0314:                                "found a node in navigator that is not an InnerNode or LeafNode");
0315:                }
0316:                _model.insertNodeInto(child, parent, i);
0317:            }
0318:
0319:            /** Removes a given <code>INavigatorItem<code> from this navigator. Removes all <code>INavigatorItem</code>s
0320:             * from this navigator that are "equal" (using <code>.equals(...)</code>) to the passed argument. Any of
0321:             * the removed documents may be returned by this method. If the NavigatorItem is found in the navigator, null
0322:             * is returned.  Only executes from event thread.
0323:             * @param doc the docment to be removed
0324:             * @return doc a document removed from this navigator as a result of invoking this method.
0325:             * @throws IllegalArgumentException if this navigator contains no document equal to doc
0326:             */
0327:            public ItemT removeDocument(ItemT doc) {
0328:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0329:                synchronized (_model) { // lock for mutation
0330:                    LeafNode<ItemT> toRemove = getNodeForDoc(doc);
0331:                    if (toRemove == null)
0332:                        return null;
0333:                    return removeNode(getNodeForDoc(doc));
0334:                }
0335:            }
0336:
0337:            /** Assumes lock on _model is already held or that it is being run in the event thread. */
0338:            private LeafNode<ItemT> getNodeForDoc(ItemT doc) {
0339:                //    synchronized(_model) { 
0340:                return _doc2node.get(doc);
0341:                //    }
0342:            }
0343:
0344:            /** Only takes in nodes that have an INavigatorItem as their object; assumes _model lock is already held.
0345:             *  Only executes in event thread. */
0346:            private ItemT removeNode(LeafNode<ItemT> node) {
0347:                DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node
0348:                        .getParent();
0349:                _model.removeNodeFromParent(node);
0350:                _doc2node.remove(node.getData());
0351:                cleanFolderNode(parent);
0352:                return node.getData();
0353:            }
0354:
0355:            /** If the given node is an InnerNode with no children, it removes it from the tree.  If the given node is a leaf or
0356:             * the root, it does nothing to it.  Assumes that _model lock is already held.  Only executes in the event thread.
0357:             */
0358:            private void cleanFolderNode(DefaultMutableTreeNode node) {
0359:                if (node instanceof  InnerNode && node.getChildCount() == 0) {
0360:                    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node
0361:                            .getParent();
0362:                    _model.removeNodeFromParent(node);
0363:                    @SuppressWarnings("unchecked")
0364:                    InnerNode<?, ItemT> typedNode = (InnerNode<?, ItemT>) node;
0365:                    _path2node.removeKey(typedNode);
0366:                    cleanFolderNode(parent);
0367:                }
0368:            }
0369:
0370:            /** Resets a given <code>INavigatorItem<code> in the tree.  Updates the placement of the item and its display
0371:             * to reflect any changes made in the model.  Only executes in the event thread.
0372:             * Note: full synchronization commented out because this operation is only performed in the event thread.  The 
0373:             * synchronized sections must be atomic but the rest of the code can run concurrently with read operations in 
0374:             * other threads.
0375:             * @param doc the document to be refreshed
0376:             * @param path the path to the parent folder for this document
0377:             * @throws IllegalArgumentException if this navigator contains no document equal to doc.
0378:             */
0379:            public void refreshDocument(ItemT doc, String path) {
0380:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0381:                //    synchronized(_model) {
0382:                LeafNode<ItemT> node = _doc2node.get(doc);
0383:                InnerNode<?, ?> oldParent;
0384:                if (node == null) { // document has not yet been entered in tree
0385:                    addDocument(doc, path);
0386:                    return;
0387:                }
0388:
0389:                InnerNode<?, ?> p = (InnerNode<?, ?>) node.getParent();
0390:                oldParent = p;
0391:
0392:                // Check to see if the new parent (could be same) exists already
0393:                String newPath = path;
0394:
0395:                if (newPath.length() > 0) {
0396:                    if (newPath.substring(0, 1).equals("/"))
0397:                        newPath = newPath.substring(1);
0398:                    if (!newPath.substring(newPath.length() - 1).equals("/"))
0399:                        newPath = newPath + "/";
0400:                }
0401:
0402:                InnerNode<?, ItemT> newParent = _path2node.getValue(newPath); // node that should be parent
0403:
0404:                if (newParent == oldParent) { // no mutation has occurred before this point because oldParent != null
0405:                    if (!node.toString().equals(doc.getName())) { // document has changed name?
0406:                        synchronized (_model) {
0407:                            LeafNode<ItemT> newLeaf = new LeafNode<ItemT>(doc);
0408:                            _doc2node.put(doc, newLeaf);
0409:                            insertNodeSortedInto(newLeaf, newParent);
0410:                            _model.removeNodeFromParent(node);
0411:                        }
0412:                    }
0413:                    // don't do anything if its name or parents haven't changed
0414:                } else { // document has moved within tree
0415:                    synchronized (_model) {
0416:                        removeNode(node);
0417:                        addDocument(doc, path);
0418:                    }
0419:                }
0420:                //    }
0421:            }
0422:
0423:            /** Sets the specified document to be active (current).  Only executes in the event thread. */
0424:            public void setActiveDoc(ItemT doc) {
0425:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0426:                //    synchronized (_model) {  // lock out mutation
0427:                DefaultMutableTreeNode node = _doc2node.get(doc);
0428:                if (node == null)
0429:                    return; // doc is not in the navigator
0430:                if (node == _current)
0431:                    return; // current doc is the active doc
0432:                    //      if (_doc2node.containsKey(doc);) {  // this test is obviously true since node == _doc2node.get(doc)
0433:                TreeNode[] nodes = node.getPath();
0434:                TreePath path = new TreePath(nodes);
0435:                expandPath(path);
0436:                setSelectionPath(path); // fires _gainVisitor in AbstractGlobalModel
0437:                scrollPathToVisible(path);
0438:                //      }
0439:                //    }
0440:            }
0441:
0442:            /** Returns a typed equivalent to {@code next.getUserObject()}.  Assumes the DefaultMutableTreeNode
0443:             * is a leaf node in _model and thus, if parameterized, would have type ItemT.  This is a workaround for
0444:             * the lack of a generic implementation of TreeModel and TreeNode.  If those classes become generified,
0445:             * this code will no longer be necessary.
0446:             */
0447:            private ItemT getNodeUserObject(DefaultMutableTreeNode n) {
0448:                @SuppressWarnings("unchecked")
0449:                ItemT result = (ItemT) n.getUserObject();
0450:                return result;
0451:            }
0452:
0453:            /** Returns the next document in the collection (using enumeration order).  Executes in any thread.
0454:             *  @param doc the INavigatorItem of interest
0455:             *  @return the INavigatorItem which comes after doc
0456:             */
0457:            public ItemT getNext(ItemT doc) {
0458:                synchronized (_model) { // locks out mutation
0459:                    DefaultMutableTreeNode node = _doc2node.get(doc);
0460:                    if (node == null)
0461:                        return doc; // doc may not be contained in navigator
0462:                    // TODO: check for "package" case
0463:                    DefaultMutableTreeNode next = node.getNextLeaf();
0464:                    if (next == null || next == _model.getRoot()) {
0465:                        return doc;
0466:                    } else {
0467:                        return getNodeUserObject(next);
0468:                    }
0469:                }
0470:            }
0471:
0472:            /** Returns the previous document in the collection (using enumeration order).  Executes in any thread.
0473:             * @param doc the INavigatorItem of interest
0474:             * @return the INavigatorItem which comes before doc
0475:             */
0476:            public ItemT getPrevious(ItemT doc) {
0477:                synchronized (_model) { // locks out mutation
0478:                    DefaultMutableTreeNode node = _doc2node.get(doc);
0479:                    if (node == null)
0480:                        return doc; // doc may not be contained in navigator
0481:                    // TODO: check for "package" case
0482:                    DefaultMutableTreeNode prev = node.getPreviousLeaf();
0483:                    if (prev == null || prev == _model.getRoot()) {
0484:                        return doc;
0485:                    } else {
0486:                        return getNodeUserObject(prev);
0487:                    }
0488:                }
0489:            }
0490:
0491:            /** Returns the first document in the collection (using enumeration order).  Executes in any thread.
0492:             *  @return the INavigatorItem which comes before doc
0493:             */
0494:            public ItemT getFirst() {
0495:                synchronized (_model) { // locks out mutation
0496:                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) _model
0497:                            .getRoot();
0498:                    return getNodeUserObject(root.getFirstLeaf());
0499:                }
0500:            }
0501:
0502:            /** Returns the last document in the collection (using enumeration order).  Executes in any thread.
0503:             *  @return the INavigatorItem which comes before doc
0504:             */
0505:            public ItemT getLast() {
0506:                synchronized (_model) { // locks out mutation
0507:                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) _model
0508:                            .getRoot();
0509:                    return getNodeUserObject(root.getLastLeaf());
0510:                }
0511:            }
0512:
0513:            /** Tests to see if a given document is contained in this navigator.  Executes in any thread.
0514:             *  @param doc the document to test for containment.
0515:             *  @return <code>true</code> if this navigator contains a document that is "equal" (as tested by the
0516:             *          <code>equals</code< method) to the passed document, else <code>false</code>.
0517:             */
0518:            public boolean contains(ItemT doc) {
0519:                synchronized (_model) {
0520:                    return _doc2node.containsKey(doc);
0521:                } // locks out mutation
0522:            }
0523:
0524:            /** Tests to see if a given document is contained in this navigator.  Only executes in event thread.*/
0525:            public boolean _contains(ItemT doc) {
0526:                return _doc2node.containsKey(doc);
0527:            }
0528:
0529:            /** Returns all the <code>IDocuments</code> contained in this navigator. Does not assert any type of ordering on 
0530:             * the returned structure.  Executes in any thread.
0531:             * @return an <code>INavigatorItem<code> enumeration of this navigator's contents.
0532:             */
0533:            public Enumeration<ItemT> getDocuments() {
0534:
0535:                final Vector<ItemT> list = new Vector<ItemT>(getDocumentCount()); // Use Vector because it implements an Enumeration
0536:
0537:                synchronized (_model) { // locks out mutation
0538:                    // e has a raw type because depthFirstEnumeration() has a raw type signature
0539:                    Enumeration e = ((DefaultMutableTreeNode) _model.getRoot())
0540:                            .depthFirstEnumeration();
0541:
0542:                    while (e.hasMoreElements()) {
0543:                        DefaultMutableTreeNode node = (DefaultMutableTreeNode) e
0544:                                .nextElement();
0545:                        if (node.isLeaf() && node != _model.getRoot()) {
0546:                            list.add(getNodeUserObject(node));
0547:                        }
0548:                    }
0549:                }
0550:                return list.elements();
0551:            }
0552:
0553:            /** Returns all the <code>IDocuments</code> contained in the specified bin.
0554:             * @param binName name of bin
0555:             * @return an <code>INavigatorItem<code> enumeration of this navigator's contents.
0556:             */
0557:            public Enumeration<ItemT> getDocumentsInBin(String binName) {
0558:                final Vector<ItemT> list = new Vector<ItemT>();
0559:
0560:                synchronized (_model) { // locks out mutation
0561:                    for (GroupNode<ItemT> gn : _roots) {
0562:                        if (gn.getData().equals(binName)) {
0563:                            // found the bin with the right name
0564:                            // e has a raw type because depthFirstEnumeration() has a raw type signature
0565:                            Enumeration e = gn.depthFirstEnumeration();
0566:
0567:                            while (e.hasMoreElements()) {
0568:                                DefaultMutableTreeNode node = (DefaultMutableTreeNode) e
0569:                                        .nextElement();
0570:                                if (node.isLeaf() && node != _model.getRoot()) {
0571:                                    list.add(getNodeUserObject(node));
0572:                                }
0573:                            }
0574:                        }
0575:                    }
0576:                }
0577:
0578:                return list.elements();
0579:            }
0580:
0581:            /** Returns the number of <code>IDocuments</code> contained by this <code>IDocumentNavigator</code>
0582:             * Not synchronized on the assumption that size field of a HashMap always has a legitimate
0583:             * value (either the size of the current state or the size of its state before some concurrent
0584:             * operation started.  Executes in any thread.  Assume size() always returns a valid (perhaps stale) value.
0585:             * @return the number of documents within this navigator.
0586:             */
0587:            public int getDocumentCount() {
0588:                return _doc2node.size();
0589:            }
0590:
0591:            /** Returns whether this <code>IDocumentNavigator</code> contains any <code>IDocuments</code>.
0592:             * @return <code>true</code> if this navigator contains one or more documents, else <code>false</code>.
0593:             * Executes in any thread.  Assume isEmpty() always returns a valid (perhaps stale) value.
0594:             */
0595:            public boolean isEmpty() {
0596:                return _doc2node.isEmpty();
0597:            }
0598:
0599:            /** Removes all <code>IDocuments</code> from this <code>IDocumentNavigator</code>.  Only executes in event thread. */
0600:            public void clear() {
0601:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0602:                synchronized (_model) {
0603:                    _doc2node.clear();
0604:                    ((DefaultMutableTreeNode) _model.getRoot())
0605:                            .removeAllChildren();
0606:                }
0607:            }
0608:
0609:            /** Adds an <code>INavigationListener</code> to this navigator. After invoking this method, the passed 
0610:             * listener will be eligible for observing this navigator. If the provided listener is already observing 
0611:             * this navigator (as tested by the == operator), no action is taken.  Only executes in event thread.
0612:             * @param listener the listener to be added to this navigator.
0613:             */
0614:            public void addNavigationListener(
0615:                    INavigationListener<? super  ItemT> listener) {
0616:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0617:                synchronized (_model) {
0618:                    navListeners.add(listener);
0619:                } // locks out access during mutation
0620:            }
0621:
0622:            /** Removes the given listener from observing this navigator. After invoking this method, all observers 
0623:             * watching this navigator "equal" (as tested by the == operator) will no longer receive observable dispatches.
0624:             * Only executes in event thread.
0625:             * @param listener the listener to be removed from this navigator
0626:             */
0627:            public void removeNavigationListener(
0628:                    INavigationListener<? super  ItemT> listener) {
0629:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0630:                synchronized (_model) {
0631:                    navListeners.remove(listener);
0632:                }
0633:            }
0634:
0635:            /** Returns a collection of all navigator listeners. Note: this is a dangerous method since it exposes a shared data 
0636:             * structure that must be synchronized with _model.
0637:             */
0638:            public Collection<INavigationListener<? super  ItemT>> getNavigatorListeners() {
0639:                return navListeners;
0640:            }
0641:
0642:            /** Standard visitor pattern.   Only used within this class.
0643:             *  @param algo the visitor to run
0644:             *  @param input the input for the visitor
0645:             */
0646:            public <InType, ReturnType> ReturnType execute(
0647:                    IDocumentNavigatorAlgo<ItemT, InType, ReturnType> algo,
0648:                    InType input) {
0649:                return algo.forTree(this , input);
0650:            }
0651:
0652:            /** Called whenever the value of the selection changes.  Only runs in event thread. Runs _gainVisitor in global model
0653:             * @param e the event that characterizes the change.
0654:             */
0655:            public void valueChanged(TreeSelectionEvent e) {
0656:                Object treeNode = this .getLastSelectedPathComponent();
0657:                if (treeNode == null || !(treeNode instanceof  NodeData))
0658:                    return;
0659:                @SuppressWarnings("unchecked")
0660:                NodeData<ItemT> newSelection = (NodeData<ItemT>) treeNode;
0661:                if (_current != newSelection) {
0662:                    for (INavigationListener<? super  ItemT> listener : navListeners) {
0663:                        listener.lostSelection(_current,
0664:                                isNextChangeModelInitiated());
0665:                        listener.gainedSelection(newSelection,
0666:                                isNextChangeModelInitiated());
0667:                    }
0668:                    _current = newSelection;
0669:                }
0670:
0671:                setNextChangeModelInitiated(false);
0672:            }
0673:
0674:            /** Returns a renderer for this object. */
0675:            public Component getRenderer() {
0676:                return _renderer;
0677:            }
0678:
0679:            /** The cell renderer for this tree. Only runs in event thread. */
0680:            private class CustomTreeCellRenderer extends
0681:                    DefaultTreeCellRenderer {
0682:
0683:                /** Rreturns the component for a cell
0684:                 * @param tree
0685:                 */
0686:                public Component getTreeCellRendererComponent(JTree tree,
0687:                        Object value, boolean sel, boolean isExpanded,
0688:                        boolean leaf, int row, boolean hasFocus) {
0689:
0690:                    // changing last argument from hasFocus to false appears to fix a focus bug when selecting a new document
0691:                    super .getTreeCellRendererComponent(tree, value, sel,
0692:                            isExpanded, leaf, row, false);
0693:
0694:                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
0695:                    if (node instanceof  RootNode && _rootIcon != null)
0696:                        setIcon(_rootIcon);
0697:
0698:                    else if (node instanceof  LeafNode) {
0699:                        ItemT doc = getNodeUserObject(node);
0700:                        if (leaf && _displayManager != null) {
0701:                            setIcon(_displayManager.getIcon(doc));
0702:                            setText(_displayManager.getName(doc));
0703:                        }
0704:                    }
0705:                    return this ;
0706:                }
0707:            }
0708:
0709:            /** Selects the document at the x,y coordinate of the navigator pane and sets it to be the currently active 
0710:             *  document.  Only runs in event thread. O
0711:             *  @param x the x coordinate of the navigator pane
0712:             *  @param y the y coordinate of the navigator pane
0713:             */
0714:            public boolean selectDocumentAt(int x, int y) {
0715:                //    synchronized (_model) {
0716:                TreePath path = getPathForLocation(x, y);
0717:                if (path == null)
0718:                    return false;
0719:                else {
0720:                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
0721:                            .getLastPathComponent();
0722:                    if (node instanceof  LeafNode) {
0723:                        this .expandPath(path);
0724:                        this .setSelectionPath(path);
0725:                        this .scrollPathToVisible(path);
0726:                        return true;
0727:                    } else if (node instanceof  InnerNode) {
0728:                        this .expandPath(path);
0729:                        this .setSelectionPath(path);
0730:                        this .scrollPathToVisible(path);
0731:                        return true;
0732:                    } else if (node instanceof  RootNode) {
0733:                        this .expandPath(path);
0734:                        this .setSelectionPath(path);
0735:                        this .scrollPathToVisible(path);
0736:                        return true;
0737:                    } else
0738:                        return false;
0739:                }
0740:                //    }
0741:            }
0742:
0743:            /** Returns true if the item at the x,y coordinate of the navigator pane is currently selected.
0744:             * Only runs in event thread. O
0745:             * @param x the x coordinate of the navigator pane
0746:             * @param y the y coordinate of the navigator pane
0747:             * @return true if the item is currently selected
0748:             */
0749:            public boolean isSelectedAt(int x, int y) {
0750:                TreePath path = getPathForLocation(x, y);
0751:                if (path == null)
0752:                    return false;
0753:                TreePath[] ps = getSelectionPaths();
0754:                if (ps == null) {
0755:                    return false;
0756:                }
0757:                for (TreePath p : ps) {
0758:                    if (path.equals(p)) {
0759:                        return true;
0760:                    }
0761:                }
0762:                return false;
0763:            }
0764:
0765:            /** @return true if at least one group of INavigatorItems is selected.  Only runs in event thread. */
0766:            public boolean isGroupSelected() {
0767:                return getGroupSelectedCount() != 0;
0768:            }
0769:
0770:            /** @return the number of groups selected. Only runs in event thread. */
0771:            public int getGroupSelectedCount() {
0772:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0773:                int count = 0;
0774:                TreePath[] ps = getSelectionPaths();
0775:                if (ps == null) {
0776:                    return 0;
0777:                }
0778:                for (TreePath p : ps) {
0779:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0780:                    if (n instanceof  InnerNode) {
0781:                        ++count;
0782:                    }
0783:                }
0784:                return count;
0785:            }
0786:
0787:            /** @return the folders currently selected. Only runs in event thread. */
0788:            public java.util.List<File> getSelectedFolders() {
0789:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0790:                ArrayList<File> l = new ArrayList<File>();
0791:                TreePath[] ps = getSelectionPaths();
0792:                if (ps == null) {
0793:                    return l;
0794:                }
0795:                for (TreePath p : ps) {
0796:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0797:                    if (n instanceof  FileNode) {
0798:                        l.add(((FileNode) n).getData());
0799:                    }
0800:                }
0801:                return l;
0802:            }
0803:
0804:            /** @return true if at least one document is selected.  Only runs in event thread. */
0805:            public boolean isDocumentSelected() {
0806:                return getDocumentSelectedCount() != 0;
0807:            }
0808:
0809:            /** @return the number of documents selected. Only runs in event thread. */
0810:            public int getDocumentSelectedCount() {
0811:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0812:                int count = 0;
0813:                TreePath[] ps = getSelectionPaths();
0814:                if (ps == null) {
0815:                    return 0;
0816:                }
0817:                for (TreePath p : ps) {
0818:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0819:                    if (n instanceof  LeafNode) {
0820:                        ++count;
0821:                    }
0822:                }
0823:                return count;
0824:            }
0825:
0826:            /** @return the documents currently selected. Only runs in event thread. */
0827:            @SuppressWarnings("unchecked")
0828:            public java.util.List<ItemT> getSelectedDocuments() {
0829:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0830:                ArrayList<ItemT> l = new ArrayList<ItemT>();
0831:                TreePath[] ps = getSelectionPaths();
0832:                if (ps == null) {
0833:                    return l;
0834:                }
0835:                for (TreePath p : ps) {
0836:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0837:                    if (n instanceof  LeafNode) {
0838:                        l.add((ItemT) ((LeafNode) n).getData());
0839:                    }
0840:                }
0841:                return l;
0842:            }
0843:
0844:            /** Returns true if at least one top level group is selected.  Only runs in event thread. */
0845:            public boolean isTopLevelGroupSelected() {
0846:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0847:                TreePath[] ps = getSelectionPaths();
0848:                if (ps == null) {
0849:                    return false;
0850:                }
0851:                for (TreePath p : ps) {
0852:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0853:                    if (n instanceof  GroupNode) {
0854:                        return true;
0855:                    }
0856:                }
0857:                return false;
0858:            }
0859:
0860:            /** Returns true if the root is selected. Only runs in event thread. */
0861:            public boolean isRootSelected() {
0862:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0863:                TreePath[] ps = getSelectionPaths();
0864:                if (ps == null) {
0865:                    return false;
0866:                }
0867:                for (TreePath p : ps) {
0868:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0869:                    if (n == _model.getRoot()) {
0870:                        return true;
0871:                    }
0872:                }
0873:                return false;
0874:            }
0875:
0876:            /** Returns the names of the top level groups that the selected items descend from.  Only runs in event thread. */
0877:            public java.util.Set<String> getNamesOfSelectedTopLevelGroup()
0878:                    throws GroupNotSelectedException {
0879:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0880:
0881:                HashSet<String> names = new HashSet<String>();
0882:                LinkedList<GroupNode<ItemT>> roots = new LinkedList<GroupNode<ItemT>>(
0883:                        _roots);
0884:
0885:                TreePath[] ps = getSelectionPaths();
0886:                if (ps != null) {
0887:                    for (TreePath p : ps) {
0888:                        if (p.getLastPathComponent() instanceof  DefaultMutableTreeNode) {
0889:                            DefaultMutableTreeNode n = (DefaultMutableTreeNode) p
0890:                                    .getLastPathComponent();
0891:
0892:                            for (GroupNode<ItemT> gn : roots) {
0893:                                if (gn.isNodeDescendant(n)) {
0894:                                    // n is a descendent of gn; add the name of the group node
0895:                                    names.add(gn.getData());
0896:                                    // this group node definitely contains selected items, no need to check it again;
0897:                                    // remove it from the list of roots to consider
0898:                                    roots.remove(gn);
0899:                                    break;
0900:                                }
0901:                            }
0902:                        }
0903:                    }
0904:                }
0905:
0906:                if (names.isEmpty()) {
0907:                    throw new GroupNotSelectedException(
0908:                            "there is no top level group for the root of the tree");
0909:                }
0910:
0911:                return names;
0912:            }
0913:
0914:            /** Returns the currently selected leaf node, or null if the selected node is not a leaf. Only reads a single
0915:             * volatile field that always has a valid value.  Thread safe. 
0916:             */
0917:            public ItemT getCurrent() {
0918:                NodeData<ItemT> current = _current;
0919:                if (current == null)
0920:                    return null;
0921:                return current.execute(_leafVisitor);
0922:            }
0923:
0924:            /** Returns the model lock. */
0925:            public Object getModelLock() {
0926:                return _model;
0927:            }
0928:
0929:            private final NodeDataVisitor<ItemT, ItemT> _leafVisitor = new NodeDataVisitor<ItemT, ItemT>() {
0930:                public ItemT fileCase(File f, Object... p) {
0931:                    return null;
0932:                }
0933:
0934:                public ItemT stringCase(String s, Object... p) {
0935:                    return null;
0936:                }
0937:
0938:                public ItemT itemCase(ItemT ini, Object... p) {
0939:                    return ini;
0940:                }
0941:            };
0942:
0943:            /** @return true if the INavigatorItem is in a selected group, if at least
0944:             * one group is selected.  Only runs in event thread. */
0945:            public boolean isSelectedInGroup(ItemT i) {
0946:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0947:
0948:                TreePath[] ps = getSelectionPaths();
0949:                if (ps == null) {
0950:                    return false;
0951:                }
0952:                for (TreePath p : ps) {
0953:                    TreeNode n = (TreeNode) p.getLastPathComponent();
0954:                    TreeNode l = _doc2node.get(i);
0955:
0956:                    if (n == _model.getRoot())
0957:                        return true;
0958:
0959:                    while (l.getParent() != _model.getRoot()) {
0960:                        if (l.getParent() == n)
0961:                            return true;
0962:                        l = l.getParent();
0963:                    }
0964:                }
0965:
0966:                return false;
0967:            }
0968:
0969:            /** Adds a top level group to the navigator.  Only runs in event thread. */
0970:            public void addTopLevelGroup(String name,
0971:                    INavigatorItemFilter<? super  ItemT> f) {
0972:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0973:
0974:                if (f == null)
0975:                    throw new IllegalArgumentException(
0976:                            "parameter 'f' is not allowed to be null");
0977:                GroupNode<ItemT> n = new GroupNode<ItemT>(name, f);
0978:                _roots.add(n);
0979:            }
0980:
0981:            /******* Methods that handle expansion/collapsing of folders in tree **********/
0982:
0983:            /**  Called whenever an item in the tree has been collapsed.   Only runs in event thread (except when testing). */
0984:            public void treeCollapsed(TreeExpansionEvent event) {
0985:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0986:
0987:                Object o = event.getPath().getLastPathComponent();
0988:                if (o instanceof  InnerNode)
0989:                    ((InnerNode<?, ?>) o).setCollapsed(true);
0990:            }
0991:
0992:            /** Called whenever an item in the tree has been expanded.  Only runs in event thread. */
0993:            public void treeExpanded(TreeExpansionEvent event) {
0994:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
0995:
0996:                Object o = event.getPath().getLastPathComponent();
0997:                if (o instanceof  InnerNode)
0998:                    ((InnerNode<?, ?>) o).setCollapsed(false);
0999:            }
1000:
1001:            /** Collapses all the paths in the tree that match one of the path strings included in the given hash set.  Path 
1002:             * strings must follow a specific format in order for them to work. See the documentation of 
1003:             * <code>generatePathString</code> for information on the format of the path strings.  Only executes in event thread.
1004:             * @param paths A hash set of path strings. 
1005:             */
1006:            public void collapsePaths(String[] paths) {
1007:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
1008:
1009:                HashSet<String> set = new HashSet<String>();
1010:                for (String s : paths) {
1011:                    set.add(s);
1012:                }
1013:                collapsePaths(set);
1014:            }
1015:
1016:            /** Set variation of collapsePaths(String ...).  Private except for testing code. Only runs in event thread except
1017:             * for testing code. 
1018:             */
1019:            void collapsePaths(HashSet<String> paths) {
1020:
1021:                DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) _model
1022:                        .getRoot();
1023:                // We use a raw type here because depthFirstEnumeration() has a raw type signature
1024:                Enumeration nodes = rootNode.depthFirstEnumeration();
1025:                ArrayList<String> list = new ArrayList<String>();
1026:                while (nodes.hasMoreElements()) {
1027:                    DefaultMutableTreeNode tn = (DefaultMutableTreeNode) nodes
1028:                            .nextElement();
1029:                    if (tn instanceof  InnerNode) {
1030:                        TreePath tp = new TreePath(tn.getPath());
1031:                        String s = generatePathString(tp);
1032:                        boolean shouldCollapse = paths.contains(s);
1033:                        if (shouldCollapse) {
1034:                            collapsePath(tp);
1035:                        }
1036:                    }
1037:                }
1038:            }
1039:
1040:            /** @return an array of path strings corresponding to the paths of the tree nodes that
1041:             * are currently collapsed. See the documentation of <code>generatePathString</code>
1042:             * for information on the format of the path strings.  Only runs in event thread (except when testing).
1043:             */
1044:            public String[] getCollapsedPaths() {
1045:                assert (EventQueue.isDispatchThread() || Utilities.TEST_MODE);
1046:
1047:                ArrayList<String> list = new ArrayList<String>();
1048:                //    synchronized (_model) {
1049:                DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) _model
1050:                        .getRoot();
1051:                // We use a raw type here because depthFirstEnumeration() has a raw type signature
1052:                Enumeration nodes = rootNode.depthFirstEnumeration();
1053:                /** This warning is expected **/
1054:                while (nodes.hasMoreElements()) {
1055:                    DefaultMutableTreeNode tn = (DefaultMutableTreeNode) nodes
1056:                            .nextElement();
1057:                    if (tn instanceof  InnerNode
1058:                            && ((InnerNode<?, ?>) tn).isCollapsed()) {
1059:                        TreePath tp = new TreePath(tn.getPath());
1060:                        list.add(generatePathString(tp));
1061:                    }
1062:                }
1063:                //    }
1064:                return list.toArray(new String[list.size()]);
1065:            }
1066:
1067:            /** Generates a path string for the given tree node. <p>The path string does not include the project 
1068:             * root node, but rather a period in its place. Following the "./" is one of the 3 main groups, 
1069:             * "[ Source Files ]", "[ Auxiliary ]", "[ External ]".  The nodes in the path are represented by their
1070:             * names delimited by the forward slash ("/").  The path ends with a final delimeter.
1071:             * (e.g. "./[ Source Files ]/util/docnavigation/")  Only runs in event thread.
1072:             * @return the path string for the given node in the JTree
1073:             */
1074:            public String generatePathString(TreePath tp) {
1075:                String path = "";
1076:                //    synchronized (_model) {
1077:                TreeNode root = (TreeNode) _model.getRoot();
1078:
1079:                while (tp != null) {
1080:                    TreeNode curr = (TreeNode) tp.getLastPathComponent();
1081:                    if (curr == root)
1082:                        path = "./" + path;
1083:                    else
1084:                        path = curr + "/" + path;
1085:                    tp = tp.getParentPath();
1086:                    //      }
1087:                }
1088:
1089:                return path;
1090:            }
1091:
1092:            /** If the currently selected item is not an INavigatorItem, select the one given. Only runs in event thread. */
1093:            public void requestSelectionUpdate(ItemT ini) {
1094:                //    synchronized (_model) {
1095:                if (getCurrent() == null) { // the currently selected node is not a leaf
1096:                    setActiveDoc(ini);
1097:                }
1098:                //    }
1099:            }
1100:
1101:            /** Marks the next selection change as model-initiated (true) or user-initiated (false; default). */
1102:            public void setNextChangeModelInitiated(boolean b) {
1103:                putClientProperty(MODEL_INITIATED_PROPERTY_NAME,
1104:                        b ? Boolean.TRUE : null);
1105:            }
1106:
1107:            /** @return whether the next selection change is model-initiated (true) or user-initiated (false). */
1108:            public boolean isNextChangeModelInitiated() {
1109:                return getClientProperty(MODEL_INITIATED_PROPERTY_NAME) != null;
1110:            }
1111:
1112:            //  /** Unnecessary since "modified" mark is added by the cell renderer */
1113:            //  public void activeDocumentModified() { }  
1114:
1115:            /** Drag and drop target. */
1116:            DropTarget dropTarget = new DropTarget(this , this );
1117:
1118:            /** User dragged something into the component. */
1119:            public void dragEnter(DropTargetDragEvent dropTargetDragEvent) {
1120:                DrJavaRoot.dragEnter(dropTargetDragEvent);
1121:            }
1122:
1123:            public void dragExit(DropTargetEvent dropTargetEvent) {
1124:            }
1125:
1126:            public void dragOver(DropTargetDragEvent dropTargetDragEvent) {
1127:            }
1128:
1129:            public void dropActionChanged(
1130:                    DropTargetDragEvent dropTargetDragEvent) {
1131:            }
1132:
1133:            /** User dropped something on the component. */
1134:            public synchronized void drop(
1135:                    DropTargetDropEvent dropTargetDropEvent) {
1136:                DrJavaRoot.drop(dropTargetDropEvent);
1137:            }
1138:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.