Source Code Cross Referenced for TreeView.java in  » IDE-Netbeans » openide » org » openide » explorer » view » 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 Netbeans » openide » org.openide.explorer.view 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:        package org.openide.explorer.view;
0042:
0043:        import org.openide.awt.MouseUtils;
0044:        import org.openide.explorer.ExplorerManager;
0045:        import org.openide.nodes.Children;
0046:        import org.openide.nodes.Node;
0047:        import org.openide.nodes.NodeOp;
0048:        import org.openide.util.ContextAwareAction;
0049:        import org.openide.util.Lookup;
0050:        import org.openide.util.Mutex;
0051:        import org.openide.util.NbBundle;
0052:        import org.openide.util.RequestProcessor;
0053:        import org.openide.util.Utilities;
0054:        import org.openide.util.WeakListeners;
0055:        import org.openide.util.actions.SystemAction;
0056:        import org.openide.util.lookup.Lookups;
0057:        import org.openide.util.lookup.ProxyLookup;
0058:        import java.awt.Container;
0059:        import java.awt.Cursor;
0060:        import java.awt.Dimension;
0061:        import java.awt.Font;
0062:        import java.awt.Graphics;
0063:        import java.awt.Insets;
0064:        import java.awt.Point;
0065:        import java.awt.Rectangle;
0066:        import java.awt.Toolkit;
0067:        import java.awt.datatransfer.Clipboard;
0068:        import java.awt.datatransfer.DataFlavor;
0069:        import java.awt.datatransfer.Transferable;
0070:        import java.awt.dnd.Autoscroll;
0071:        import java.awt.dnd.DnDConstants;
0072:        import java.awt.event.ActionEvent;
0073:        import java.awt.event.ActionListener;
0074:        import java.awt.event.FocusEvent;
0075:        import java.awt.event.FocusListener;
0076:        import java.awt.event.InputEvent;
0077:        import java.awt.event.KeyAdapter;
0078:        import java.awt.event.KeyEvent;
0079:        import java.awt.event.KeyListener;
0080:        import java.awt.event.MouseAdapter;
0081:        import java.awt.event.MouseEvent;
0082:        import java.beans.PropertyChangeEvent;
0083:        import java.beans.PropertyChangeListener;
0084:        import java.beans.PropertyVetoException;
0085:        import java.beans.VetoableChangeListener;
0086:        import java.util.ArrayList;
0087:        import java.util.Arrays;
0088:        import java.util.Iterator;
0089:        import java.util.List;
0090:        import java.util.Set;
0091:        import java.util.HashSet;
0092:        import java.util.logging.Level;
0093:        import java.util.logging.Logger;
0094:        import javax.accessibility.AccessibleContext;
0095:        import javax.swing.AbstractAction;
0096:        import javax.swing.Action;
0097:        import javax.swing.BorderFactory;
0098:        import javax.swing.BoxLayout;
0099:        import javax.swing.JComponent;
0100:        import javax.swing.JLabel;
0101:        import javax.swing.JMenu;
0102:        import javax.swing.JPanel;
0103:        import javax.swing.JPopupMenu;
0104:        import javax.swing.JScrollPane;
0105:        import javax.swing.JTextField;
0106:        import javax.swing.JTree;
0107:        import javax.swing.JViewport;
0108:        import javax.swing.KeyStroke;
0109:        import javax.swing.SwingUtilities;
0110:        import javax.swing.ToolTipManager;
0111:        import javax.swing.TransferHandler;
0112:        import javax.swing.UIManager;
0113:        import javax.swing.event.DocumentEvent;
0114:        import javax.swing.event.DocumentListener;
0115:        import javax.swing.event.TreeExpansionEvent;
0116:        import javax.swing.event.TreeExpansionListener;
0117:        import javax.swing.event.TreeModelEvent;
0118:        import javax.swing.event.TreeModelListener;
0119:        import javax.swing.event.TreeSelectionEvent;
0120:        import javax.swing.event.TreeSelectionListener;
0121:        import javax.swing.event.TreeWillExpandListener;
0122:        import javax.swing.plaf.UIResource;
0123:        import javax.swing.text.Position;
0124:        import javax.swing.tree.ExpandVetoException;
0125:        import javax.swing.tree.RowMapper;
0126:        import javax.swing.tree.TreeModel;
0127:        import javax.swing.tree.TreeNode;
0128:        import javax.swing.tree.TreePath;
0129:        import javax.swing.tree.TreeSelectionModel;
0130:
0131:        /**
0132:         * Base class for tree-style explorer views. 
0133:         * @see BeanTreeView
0134:         * @see ContextTreeView
0135:         */
0136:        public abstract class TreeView extends JScrollPane {
0137:            static {
0138:                // Workaround for issue #42794 on JDK1.5
0139:                UIManager.put("Tree.scrollsHorizontallyAndVertically",
0140:                        Boolean.TRUE);
0141:            }
0142:
0143:            //
0144:            // static fields
0145:            //
0146:
0147:            /** generated Serialized Version UID */
0148:            static final long serialVersionUID = -1639001987693376168L;
0149:
0150:            /** How long it takes before collapsed nodes are released from the tree's cache
0151:             */
0152:            private static final int TIME_TO_COLLAPSE = (System
0153:                    .getProperty("netbeans.debug.heap") != null) ? 0 : 15000;
0154:
0155:            /** Minimum width of this component. */
0156:            private static final int MIN_TREEVIEW_WIDTH = 400;
0157:
0158:            /** Minimum height of this component. */
0159:            private static final int MIN_TREEVIEW_HEIGHT = 400;
0160:
0161:            //GTK Look and feel hack
0162:            private static boolean isSynth = UIManager.getLookAndFeel()
0163:                    .getClass().getName()
0164:                    .indexOf("com.sun.java.swing.plaf.gtk") != -1;
0165:
0166:            //
0167:            // components
0168:            //
0169:
0170:            /** Main <code>JTree</code> component. */
0171:            transient protected JTree tree;
0172:
0173:            /** model */
0174:            transient NodeTreeModel treeModel;
0175:
0176:            /** Explorer manager, valid when this view is showing */
0177:            transient ExplorerManager manager;
0178:
0179:            // Attributes
0180:
0181:            /** Mouse and action listener. */
0182:            transient PopupSupport defaultActionListener;
0183:
0184:            /** Property indicating whether the default action is enabled. */
0185:            transient boolean defaultActionEnabled;
0186:
0187:            /** not null if popup menu enabled */
0188:            transient PopupAdapter popupListener;
0189:
0190:            /** the most important listener (on four types of events */
0191:            transient TreePropertyListener managerListener = null;
0192:
0193:            /** weak variation of the listener for property change on the explorer manager */
0194:            transient PropertyChangeListener wlpc;
0195:
0196:            /** weak variation of the listener for vetoable change on the explorer manager */
0197:            transient VetoableChangeListener wlvc;
0198:
0199:            /** true if drag support is active */
0200:            private transient boolean dragActive = true;
0201:
0202:            /** true if drop support is active */
0203:            private transient boolean dropActive = true;
0204:
0205:            /** Drag support */
0206:            transient TreeViewDragSupport dragSupport;
0207:
0208:            /** Drop support */
0209:            transient TreeViewDropSupport dropSupport;
0210:            transient boolean dropTargetPopupAllowed = true;
0211:            transient private Container contentPane;
0212:            transient private List storeSelectedPaths;
0213:
0214:            // default DnD actions
0215:            transient private int allowedDragActions = DnDConstants.ACTION_COPY_OR_MOVE
0216:                    | DnDConstants.ACTION_REFERENCE;
0217:            transient private int allowedDropActions = DnDConstants.ACTION_COPY_OR_MOVE
0218:                    | DnDConstants.ACTION_REFERENCE;
0219:
0220:            /**
0221:             * Whether the quick search uses prefix or substring. 
0222:             * Defaults to false meaning prefix is used.
0223:             */
0224:            transient private boolean quickSearchUsingSubstring = false;
0225:
0226:            /** Constructor.
0227:             */
0228:            public TreeView() {
0229:                this (true, true);
0230:            }
0231:
0232:            /** Constructor.
0233:             * @param defaultAction should double click on a node open its default action?
0234:             * @param popupAllowed should right-click open popup?
0235:             */
0236:            public TreeView(boolean defaultAction, boolean popupAllowed) {
0237:                initializeTree();
0238:
0239:                //        // activation of drop target
0240:                //        if (DragDropUtilities.dragAndDropEnabled) {
0241:                //            setdroptExplorerDnDManager.getDefault().addFutureDropTarget(this);
0242:                //
0243:                //            // note: drag target is activated on focus gained
0244:                //        }
0245:                setDropTarget(DragDropUtilities.dragAndDropEnabled);
0246:
0247:                setPopupAllowed(popupAllowed);
0248:                setDefaultActionAllowed(defaultAction);
0249:
0250:                Dimension dim = null;
0251:
0252:                try {
0253:                    dim = getPreferredSize();
0254:
0255:                    if (dim == null) {
0256:                        dim = new Dimension(MIN_TREEVIEW_WIDTH,
0257:                                MIN_TREEVIEW_HEIGHT);
0258:                    }
0259:                } catch (NullPointerException npe) {
0260:                    dim = new Dimension(MIN_TREEVIEW_WIDTH, MIN_TREEVIEW_HEIGHT);
0261:                }
0262:
0263:                if (dim.width < MIN_TREEVIEW_WIDTH) {
0264:                    dim.width = MIN_TREEVIEW_WIDTH;
0265:                }
0266:
0267:                if (dim.height < MIN_TREEVIEW_HEIGHT) {
0268:                    dim.height = MIN_TREEVIEW_HEIGHT;
0269:                }
0270:
0271:                setPreferredSize(dim);
0272:            }
0273:
0274:            public void updateUI() {
0275:                super .updateUI();
0276:
0277:                //On GTK L&F, the viewport border must be set to empty (not null!) or we still get border buildup
0278:                setViewportBorder(BorderFactory.createEmptyBorder());
0279:                setBorder(BorderFactory.createEmptyBorder());
0280:            }
0281:
0282:            /** Initializes the tree & model.
0283:             * [dafe] Horrible technique - overridable method called from constructor
0284:             * may result in subclass code invoked when this object is not fully
0285:             * constructed.
0286:             * However I don't have enough knowledge about this code to change it.
0287:             */
0288:            void initializeTree() {
0289:                // initilizes the JTree
0290:                treeModel = createModel();
0291:                treeModel.addView(this );
0292:
0293:                tree = new ExplorerTree(treeModel);
0294:
0295:                NodeRenderer rend = new NodeRenderer();
0296:                tree.setCellRenderer(rend);
0297:                tree.putClientProperty("JTree.lineStyle", "Angled"); // NOI18N
0298:                setViewportView(tree);
0299:
0300:                // Init of the editor
0301:                tree.setCellEditor(new TreeViewCellEditor(tree));
0302:                tree.setEditable(true);
0303:
0304:                // set selection mode to DISCONTIGUOUS_TREE_SELECTION as default
0305:                setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
0306:
0307:                ToolTipManager.sharedInstance().registerComponent(tree);
0308:
0309:                // init listener & attach it to closing of
0310:                managerListener = new TreePropertyListener();
0311:                tree.addTreeExpansionListener(managerListener);
0312:                tree.addTreeWillExpandListener(managerListener);
0313:
0314:                // do not care about focus
0315:                setRequestFocusEnabled(false);
0316:
0317:                defaultActionListener = new PopupSupport();
0318:                getInputMap(JTree.WHEN_FOCUSED).put(
0319:                        KeyStroke.getKeyStroke(KeyEvent.VK_F10,
0320:                                KeyEvent.SHIFT_DOWN_MASK),
0321:                        "org.openide.actions.PopupAction");
0322:                getActionMap().put("org.openide.actions.PopupAction",
0323:                        defaultActionListener.popup);
0324:                tree.addFocusListener(defaultActionListener);
0325:                tree.addMouseListener(defaultActionListener);
0326:            }
0327:
0328:            /** Is it permitted to display a popup menu?
0329:             * @return <code>true</code> if so
0330:             */
0331:            public boolean isPopupAllowed() {
0332:                return popupListener != null;
0333:            }
0334:
0335:            /** Enable/disable displaying popup menus on tree view items.
0336:             * Default is enabled.
0337:             * @param value <code>true</code> to enable
0338:             */
0339:            public void setPopupAllowed(boolean value) {
0340:                if ((popupListener == null) && value) {
0341:                    // on
0342:                    popupListener = new PopupAdapter();
0343:                    tree.addMouseListener(popupListener);
0344:
0345:                    return;
0346:                }
0347:
0348:                if ((popupListener != null) && !value) {
0349:                    // off
0350:                    tree.removeMouseListener(popupListener);
0351:                    popupListener = null;
0352:
0353:                    return;
0354:                }
0355:            }
0356:
0357:            void setDropTargetPopupAllowed(boolean value) {
0358:                dropTargetPopupAllowed = value;
0359:
0360:                if (dropSupport != null) {
0361:                    dropSupport.setDropTargetPopupAllowed(value);
0362:                }
0363:            }
0364:
0365:            boolean isDropTargetPopupAllowed() {
0366:                return (dropSupport != null) ? dropSupport
0367:                        .isDropTargetPopupAllowed() : dropTargetPopupAllowed;
0368:            }
0369:
0370:            /** Does a double click invoke the default node action?
0371:             * @return <code>true</code> if so
0372:             */
0373:            public boolean isDefaultActionEnabled() {
0374:                return defaultActionEnabled;
0375:            }
0376:
0377:            /** Requests focus for the tree component. Overrides superclass method. */
0378:            public void requestFocus() {
0379:                tree.requestFocus();
0380:            }
0381:
0382:            /** Requests focus for the tree component. Overrides superclass method. */
0383:            public boolean requestFocusInWindow() {
0384:                return tree.requestFocusInWindow();
0385:            }
0386:
0387:            /** Enable/disable double click to invoke default action.
0388:             * If defaultAction is not enabled double click expand/collapse node.
0389:             * @param value <code>true</code> to enable
0390:             */
0391:            public void setDefaultActionAllowed(boolean value) {
0392:                defaultActionEnabled = value;
0393:
0394:                if (value) {
0395:                    tree
0396:                            .registerKeyboardAction(defaultActionListener,
0397:                                    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
0398:                                            0, false), JComponent.WHEN_FOCUSED);
0399:                } else {
0400:                    // Switch off.
0401:                    tree.unregisterKeyboardAction(KeyStroke.getKeyStroke(
0402:                            KeyEvent.VK_ENTER, 0, false));
0403:                }
0404:            }
0405:
0406:            /**
0407:             * Is the root node of the tree displayed?
0408:             *
0409:             * @return <code>true</code> if so
0410:             */
0411:            public boolean isRootVisible() {
0412:                return tree.isRootVisible();
0413:            }
0414:
0415:            /** Set whether or not the root node from
0416:             * the <code>TreeModel</code> is visible.
0417:             *
0418:             * @param visible <code>true</code> if it is to be displayed
0419:             */
0420:            public void setRootVisible(boolean visible) {
0421:                tree.setRootVisible(visible);
0422:                tree.setShowsRootHandles(!visible);
0423:            }
0424:
0425:            /**
0426:             * Set whether the quick search feature uses substring or prefix
0427:             * matching for the typed characters. Defaults to prefix (false).
0428:             * @since 6.11
0429:             * @param useSubstring <code>true</code> if substring search is used in quick search
0430:             */
0431:            public void setUseSubstringInQuickSearch(boolean useSubstring) {
0432:                quickSearchUsingSubstring = useSubstring;
0433:            }
0434:
0435:            /********** Support for the Drag & Drop operations *********/
0436:            /** Drag support is enabled by default.
0437:             * @return true if dragging from the view is enabled, false
0438:             * otherwise.
0439:             */
0440:            public boolean isDragSource() {
0441:                return dragActive;
0442:            }
0443:
0444:            /** Enables/disables dragging support.
0445:             * @param state true enables dragging support, false disables it.
0446:             */
0447:            public void setDragSource(boolean state) {
0448:                // create drag support if needed
0449:                if (state && (dragSupport == null)) {
0450:                    dragSupport = new TreeViewDragSupport(this , tree);
0451:                }
0452:
0453:                // activate / deactivate support according to the state
0454:                dragActive = state;
0455:
0456:                if (dragSupport != null) {
0457:                    dragSupport.activate(dragActive);
0458:                }
0459:            }
0460:
0461:            /** Drop support is enabled by default.
0462:             * @return true if dropping to the view is enabled, false
0463:             * otherwise<br>
0464:             */
0465:            public boolean isDropTarget() {
0466:                return dropActive;
0467:            }
0468:
0469:            /** Enables/disables dropping support.
0470:             * @param state true means drops into view are allowed,
0471:             * false forbids any drops into this view.
0472:             */
0473:            public void setDropTarget(boolean state) {
0474:                // create drop support if needed
0475:                if (dropActive && (dropSupport == null)) {
0476:                    dropSupport = new TreeViewDropSupport(this , tree,
0477:                            dropTargetPopupAllowed);
0478:                }
0479:
0480:                // activate / deactivate support according to the state
0481:                dropActive = state;
0482:
0483:                if (dropSupport != null) {
0484:                    dropSupport.activate(dropActive);
0485:                }
0486:            }
0487:
0488:            /** Actions constants comes from {@link java.awt.dnd.DnDConstants}.
0489:             * All actions (copy, move, link) are allowed by default.
0490:             * @return int representing set of actions which are allowed when dragging from
0491:             * asociated component.
0492:             */
0493:            public int getAllowedDragActions() {
0494:                return allowedDragActions;
0495:            }
0496:
0497:            /** Sets allowed actions for dragging
0498:             * @param actions new drag actions, using {@link java.awt.dnd.DnDConstants}
0499:             */
0500:            public void setAllowedDragActions(int actions) {
0501:                // PENDING: check parameters
0502:                allowedDragActions = actions;
0503:            }
0504:
0505:            /** Actions constants comes from {@link java.awt.dnd.DnDConstants}.
0506:             * All actions are allowed by default.
0507:             * @return int representing set of actions which are allowed when dropping
0508:             * into the asociated component.
0509:             */
0510:            public int getAllowedDropActions() {
0511:                return allowedDropActions;
0512:            }
0513:
0514:            /** Sets allowed actions for dropping.
0515:             * @param actions new allowed drop actions, using {@link java.awt.dnd.DnDConstants}
0516:             */
0517:            public void setAllowedDropActions(int actions) {
0518:                // PENDING: check parameters
0519:                allowedDropActions = actions;
0520:            }
0521:
0522:            //
0523:            // Control over expanded state
0524:            //
0525:
0526:            /** Collapses the tree under given node.
0527:             *
0528:             * @param n node to collapse
0529:             */
0530:            public void collapseNode(Node n) {
0531:                if (n == null) {
0532:                    throw new IllegalArgumentException();
0533:                }
0534:
0535:                TreePath treePath = new TreePath(treeModel
0536:                        .getPathToRoot(VisualizerNode.getVisualizer(null, n)));
0537:                tree.collapsePath(treePath);
0538:            }
0539:
0540:            /** Expandes the node in the tree.
0541:             *
0542:             * @param n node
0543:             */
0544:            public void expandNode(Node n) {
0545:                if (n == null) {
0546:                    throw new IllegalArgumentException();
0547:                }
0548:
0549:                lookupExplorerManager();
0550:
0551:                TreePath treePath = new TreePath(treeModel
0552:                        .getPathToRoot(VisualizerNode.getVisualizer(null, n)));
0553:
0554:                tree.expandPath(treePath);
0555:            }
0556:
0557:            /** Test whether a node is expanded in the tree or not
0558:             * @param n the node to test
0559:             * @return true if the node is expanded
0560:             */
0561:            public boolean isExpanded(Node n) {
0562:                TreePath treePath = new TreePath(treeModel
0563:                        .getPathToRoot(VisualizerNode.getVisualizer(null, n)));
0564:
0565:                return tree.isExpanded(treePath);
0566:            }
0567:
0568:            /** Expands all paths.
0569:             */
0570:            public void expandAll() {
0571:                int i = 0;
0572:                int j /*, k = tree.getRowCount()*/;
0573:
0574:                do {
0575:                    do {
0576:                        j = tree.getRowCount();
0577:                        tree.expandRow(i);
0578:                    } while (j != tree.getRowCount());
0579:
0580:                    i++;
0581:                } while (i < tree.getRowCount());
0582:            }
0583:
0584:            //
0585:            // Processing functions
0586:            //
0587:
0588:            public void validate() {
0589:                Children.MUTEX.readAccess(new Runnable() {
0590:                    public void run() {
0591:                        TreeView.super .validate();
0592:                    }
0593:                });
0594:            }
0595:
0596:            /** Initializes the component and lookup explorer manager.
0597:             */
0598:            public void addNotify() {
0599:                super .addNotify();
0600:                lookupExplorerManager();
0601:            }
0602:
0603:            /** Registers in the tree of components.
0604:             */
0605:            private void lookupExplorerManager() {
0606:                // Enter key in the tree
0607:                ExplorerManager newManager = ExplorerManager
0608:                        .find(TreeView.this );
0609:
0610:                if (newManager != manager) {
0611:                    if (manager != null) {
0612:                        manager.removeVetoableChangeListener(wlvc);
0613:                        manager.removePropertyChangeListener(wlpc);
0614:                    }
0615:
0616:                    manager = newManager;
0617:
0618:                    manager.addVetoableChangeListener(wlvc = WeakListeners
0619:                            .vetoableChange(managerListener, manager));
0620:                    manager.addPropertyChangeListener(wlpc = WeakListeners
0621:                            .propertyChange(managerListener, manager));
0622:
0623:                    synchronizeRootContext();
0624:                    synchronizeExploredContext();
0625:                    synchronizeSelectedNodes();
0626:                }
0627:
0628:                // Sometimes the listener is registered twice and we get the 
0629:                // selection events twice. Removing the listener before adding it
0630:                // should be a safe fix.
0631:                tree.getSelectionModel().removeTreeSelectionListener(
0632:                        managerListener);
0633:                tree.getSelectionModel().addTreeSelectionListener(
0634:                        managerListener);
0635:            }
0636:
0637:            /** Deinitializes listeners.
0638:             */
0639:            public void removeNotify() {
0640:                super .removeNotify();
0641:
0642:                tree.getSelectionModel().removeTreeSelectionListener(
0643:                        managerListener);
0644:            }
0645:
0646:            // *************************************
0647:            // Methods to be overriden by subclasses
0648:            // *************************************
0649:
0650:            /** Allows subclasses to provide own model for displaying nodes.
0651:             * @return the model to use for this view
0652:             */
0653:            protected abstract NodeTreeModel createModel();
0654:
0655:            /** Called to allow subclasses to define the behaviour when a
0656:             * node(s) are selected in the tree.
0657:             *
0658:             * @param nodes the selected nodes
0659:             * @param em explorer manager to work on (change nodes to it)
0660:             * @throws PropertyVetoException if the change cannot be done by the explorer
0661:             *    (the exception is silently consumed)
0662:             */
0663:            protected abstract void selectionChanged(Node[] nodes,
0664:                    ExplorerManager em) throws PropertyVetoException;
0665:
0666:            /** Called when explorer manager is about to change the current selection.
0667:             * The view can forbid the change if it is not able to display such
0668:             * selection.
0669:             *
0670:             * @param nodes the nodes to select
0671:             * @return false if the view is not able to change the selection
0672:             */
0673:            protected abstract boolean selectionAccept(Node[] nodes);
0674:
0675:            /** Show a given path in the screen. It depends on the kind of <code>TreeView</code>
0676:             * if the path should be expanded or just made visible.
0677:             *
0678:             * @param path the path
0679:             */
0680:            protected abstract void showPath(TreePath path);
0681:
0682:            /** Shows selection to reflect the current state of the selection in the explorer.
0683:             *
0684:             * @param paths array of paths that should be selected
0685:             */
0686:            protected abstract void showSelection(TreePath[] paths);
0687:
0688:            /** Specify whether a context menu of the explored context should be used.
0689:             * Applicable when no nodes are selected and the user wants to invoke
0690:             * a context menu (clicks right mouse button).
0691:             *
0692:             * @return <code>true</code> if so; <code>false</code> in the default implementation
0693:             */
0694:            protected boolean useExploredContextMenu() {
0695:                return false;
0696:            }
0697:
0698:            /** Check if selection of the nodes could break the selection mode set in TreeSelectionModel.
0699:             * @param nodes the nodes for selection
0700:             * @return true if the selection mode is broken */
0701:            private boolean isSelectionModeBroken(Node[] nodes) {
0702:                // if nodes are empty or single the everthing is ok
0703:                // or if discontiguous selection then everthing ok
0704:                if ((nodes.length <= 1)
0705:                        || (getSelectionMode() == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)) {
0706:                    return false;
0707:                }
0708:
0709:                // if many nodes
0710:                // brakes single selection mode
0711:                if (getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION) {
0712:                    return true;
0713:                }
0714:
0715:                // check the contiguous selection mode
0716:                TreePath[] paths = new TreePath[nodes.length];
0717:                RowMapper rowMapper = tree.getSelectionModel().getRowMapper();
0718:
0719:                // if rowMapper is null then tree bahaves as discontiguous selection mode is set
0720:                if (rowMapper == null) {
0721:                    return false;
0722:                }
0723:
0724:                ArrayList<Node> toBeExpaned = new ArrayList<Node>(3);
0725:
0726:                for (int i = 0; i < nodes.length; i++) {
0727:                    toBeExpaned.clear();
0728:
0729:                    Node n = nodes[i];
0730:
0731:                    while (n.getParentNode() != null) {
0732:                        if (!isExpanded(n)) {
0733:                            toBeExpaned.add(n);
0734:                        }
0735:
0736:                        n = n.getParentNode();
0737:                    }
0738:
0739:                    for (int j = toBeExpaned.size() - 1; j >= 0; j--) {
0740:                        expandNode((Node) toBeExpaned.get(j));
0741:                    }
0742:
0743:                    TreePath treePath = new TreePath(treeModel
0744:                            .getPathToRoot(VisualizerNode.getVisualizer(null,
0745:                                    nodes[i])));
0746:                    paths[i] = treePath;
0747:                }
0748:
0749:                int[] rows = rowMapper.getRowsForPaths(paths);
0750:
0751:                // check selection's rows
0752:                Arrays.sort(rows);
0753:
0754:                for (int i = 1; i < rows.length; i++) {
0755:                    if (rows[i] != (rows[i - 1] + 1)) {
0756:                        return true;
0757:                    }
0758:                }
0759:
0760:                // all is ok
0761:                return false;
0762:            }
0763:
0764:            //
0765:            // synchronizations
0766:            //
0767:
0768:            /** Called when selection in tree is changed.
0769:             */
0770:            final void callSelectionChanged(Node[] nodes) {
0771:                manager.removePropertyChangeListener(wlpc);
0772:                manager.removeVetoableChangeListener(wlvc);
0773:
0774:                try {
0775:                    selectionChanged(nodes, manager);
0776:                } catch (PropertyVetoException e) {
0777:                    synchronizeSelectedNodes();
0778:                } finally {
0779:                    manager.addPropertyChangeListener(wlpc);
0780:                    manager.addVetoableChangeListener(wlvc);
0781:                }
0782:            }
0783:
0784:            /** Synchronize the root context from the manager of this Explorer.
0785:             */
0786:            final void synchronizeRootContext() {
0787:                treeModel.setNode(manager.getRootContext());
0788:            }
0789:
0790:            /** Synchronize the explored context from the manager of this Explorer.
0791:             */
0792:            final void synchronizeExploredContext() {
0793:                Node n = manager.getExploredContext();
0794:
0795:                if (n != null) {
0796:                    TreePath treePath = new TreePath(treeModel
0797:                            .getPathToRoot(VisualizerNode
0798:                                    .getVisualizer(null, n)));
0799:                    showPath(treePath);
0800:                }
0801:            }
0802:
0803:            /** Sets the selection model, which must be one of
0804:             * TreeSelectionModel.SINGLE_TREE_SELECTION,
0805:             * TreeSelectionModel.CONTIGUOUS_TREE_SELECTION or
0806:             * TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION.
0807:             * <p>
0808:             * This may change the selection if the current selection is not valid
0809:             * for the new mode. For example, if three TreePaths are
0810:             * selected when the mode is changed to <code>TreeSelectionModel.SINGLE_TREE_SELECTION</code>,
0811:             * only one TreePath will remain selected. It is up to the particular
0812:             * implementation to decide what TreePath remains selected.
0813:             * Note: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION is set as default.
0814:             * @since 2.15
0815:             * @param mode selection mode
0816:             */
0817:            public void setSelectionMode(int mode) {
0818:                tree.getSelectionModel().setSelectionMode(mode);
0819:            }
0820:
0821:            /** Returns the current selection mode, one of
0822:             * <code>TreeSelectionModel.SINGLE_TREE_SELECTION</code>,
0823:             * <code>TreeSelectionModel.CONTIGUOUS_TREE_SELECTION</code> or
0824:             * <code>TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION</code>.
0825:             * @since 2.15
0826:             * @return selection mode
0827:             */
0828:            public int getSelectionMode() {
0829:                return tree.getSelectionModel().getSelectionMode();
0830:            }
0831:
0832:            //
0833:            // showing and removing the wait cursor
0834:            //
0835:            private void showWaitCursor() {
0836:                if (getRootPane() == null) {
0837:                    return;
0838:                }
0839:
0840:                contentPane = getRootPane().getContentPane();
0841:
0842:                if (SwingUtilities.isEventDispatchThread()) {
0843:                    contentPane.setCursor(Utilities
0844:                            .createProgressCursor(contentPane));
0845:                } else {
0846:                    SwingUtilities.invokeLater(new CursorR(contentPane,
0847:                            Utilities.createProgressCursor(contentPane)));
0848:                }
0849:            }
0850:
0851:            private void showNormalCursor() {
0852:                if (contentPane == null) {
0853:                    return;
0854:                }
0855:
0856:                if (SwingUtilities.isEventDispatchThread()) {
0857:                    contentPane.setCursor(null);
0858:                } else {
0859:                    SwingUtilities.invokeLater(new CursorR(contentPane, null));
0860:                }
0861:            }
0862:
0863:            private void prepareWaitCursor(final Node node) {
0864:                // check type of node
0865:                if (node == null) {
0866:                    showNormalCursor();
0867:                }
0868:
0869:                showWaitCursor();
0870:                RequestProcessor.getDefault().post(new Runnable() {
0871:
0872:                    public void run() {
0873:                        try {
0874:                            node.getChildren().getNodes(true);
0875:                        } catch (Exception e) {
0876:                            // log a exception
0877:                            Logger.getLogger(TreeView.class.getName()).log(
0878:                                    Level.WARNING, null, e);
0879:                        } finally {
0880:                            // show normal cursor above all
0881:                            showNormalCursor();
0882:                        }
0883:                    }
0884:                });
0885:            }
0886:
0887:            /** Synchronize the selected nodes from the manager of this Explorer.
0888:             * The default implementation does nothing.
0889:             */
0890:            final void synchronizeSelectedNodes() {
0891:                // #40152: if there is any scheduled change to view, perform it now
0892:                VisualizerNode.runQueue();
0893:
0894:                Node[] arr = manager.getSelectedNodes();
0895:                TreePath[] paths = new TreePath[arr.length];
0896:
0897:                for (int i = 0; i < arr.length; i++) {
0898:                    TreePath treePath = new TreePath(treeModel
0899:                            .getPathToRoot(VisualizerNode.getVisualizer(null,
0900:                                    arr[i])));
0901:                    paths[i] = treePath;
0902:                }
0903:
0904:                tree.getSelectionModel().removeTreeSelectionListener(
0905:                        managerListener);
0906:                showSelection(paths);
0907:                tree.getSelectionModel().addTreeSelectionListener(
0908:                        managerListener);
0909:            }
0910:
0911:            void scrollTreeToVisible(TreePath path, TreeNode child) {
0912:                Rectangle base = tree.getVisibleRect();
0913:                Rectangle b1 = tree.getPathBounds(path);
0914:                Rectangle b2 = tree.getPathBounds(new TreePath(treeModel
0915:                        .getPathToRoot(child)));
0916:
0917:                if ((base != null) && (b1 != null) && (b2 != null)) {
0918:                    tree.scrollRectToVisible(new Rectangle(base.x, b1.y, 1,
0919:                            b2.y - b1.y + b2.height));
0920:                }
0921:            }
0922:
0923:            private void createPopup(int xpos, int ypos, JPopupMenu popup) {
0924:                if (popup.getSubElements().length > 0) {
0925:                    popup.show(TreeView.this , xpos, ypos);
0926:                }
0927:            }
0928:
0929:            void createPopup(int xpos, int ypos) {
0930:                // bugfix #23932, don't create if it's disabled
0931:                if (isPopupAllowed()) {
0932:                    Node[] arr = manager.getSelectedNodes();
0933:
0934:                    if (arr.length == 0) {
0935:                        // Should probably not happen when shown from right-click, but may well when from S-F10.
0936:                        // Create popup menu for the root node, and make sure it is selected so that action context is correct.
0937:                        arr = new Node[] { manager.getRootContext() };
0938:
0939:                        try {
0940:                            manager.setSelectedNodes(arr);
0941:                        } catch (PropertyVetoException e) {
0942:                            assert false : e; // not permitted to be thrown
0943:                        }
0944:                    }
0945:
0946:                    Action[] actions = NodeOp.findActions(arr);
0947:
0948:                    if (actions.length > 0) {
0949:                        createPopup(xpos, ypos, Utilities.actionsToPopup(
0950:                                actions, this ));
0951:                    }
0952:                }
0953:            }
0954:
0955:            /* create standard popup menu and add newMenu to it
0956:             */
0957:            void createExtendedPopup(int xpos, int ypos, JMenu newMenu) {
0958:                Node[] ns = manager.getSelectedNodes();
0959:                JPopupMenu popup = null;
0960:
0961:                if (ns.length > 0) {
0962:                    // if any nodes are selected --> find theirs actions
0963:                    Action[] actions = NodeOp.findActions(ns);
0964:                    popup = Utilities.actionsToPopup(actions, this );
0965:                } else {
0966:                    // if none node is selected --> get context actions from view's root
0967:                    if (manager.getRootContext() != null) {
0968:                        popup = manager.getRootContext().getContextMenu();
0969:                    }
0970:                }
0971:
0972:                int cnt = 0;
0973:
0974:                if (popup == null) {
0975:                    popup = SystemAction.createPopupMenu(new SystemAction[] {});
0976:                }
0977:
0978:                popup.add(newMenu);
0979:
0980:                createPopup(xpos, ypos, popup);
0981:            }
0982:
0983:            /** Returns the the point at which the popup menu is to be showed. May return null.
0984:             * @return the point or null
0985:             */
0986:            Point getPositionForPopup() {
0987:                int i = tree.getLeadSelectionRow();
0988:
0989:                if (i < 0) {
0990:                    return null;
0991:                }
0992:
0993:                Rectangle rect = tree.getRowBounds(i);
0994:
0995:                if (rect == null) {
0996:                    return null;
0997:                }
0998:
0999:                Point p = new Point(rect.x, rect.y);
1000:
1001:                // bugfix #36984, convert point by TreeView.this
1002:                p = SwingUtilities.convertPoint(tree, p, TreeView.this );
1003:
1004:                return p;
1005:            }
1006:
1007:            static Action takeAction(Action action, Node... nodes) {
1008:                // bugfix #42843, use ContextAwareAction if possible
1009:                if (action instanceof  ContextAwareAction) {
1010:                    Lookup contextLookup = getLookupFor(nodes);
1011:
1012:                    Action contextInstance = ((ContextAwareAction) action)
1013:                            .createContextAwareInstance(contextLookup);
1014:                    assert contextInstance != action : "Cannot be same. ContextAwareAction:  "
1015:                            + action
1016:                            + ", ContextAwareInstance: "
1017:                            + contextInstance;
1018:                    action = contextInstance;
1019:                }
1020:
1021:                return action;
1022:            }
1023:
1024:            private static Lookup getLookupFor(Node... nodes) {
1025:                if (nodes.length == 1) {
1026:                    Lookup contextLookup = nodes[0].getLookup();
1027:                    Object o = contextLookup.lookup(nodes[0].getClass());
1028:                    // #55826, don't added the node twice
1029:                    if (!nodes[0].equals(o)) {
1030:                        contextLookup = new ProxyLookup(new Lookup[] {
1031:                                Lookups.singleton(nodes[0]), contextLookup });
1032:                    }
1033:                    return contextLookup;
1034:                } else {
1035:                    Lookup[] lkps = new Lookup[nodes.length];
1036:                    for (int i = 0; i < nodes.length; i++) {
1037:                        lkps[i] = nodes[i].getLookup();
1038:                    }
1039:                    Lookup contextLookup = new ProxyLookup(lkps);
1040:                    Set<Node> toAdd = new HashSet<Node>(Arrays.asList(nodes));
1041:                    toAdd.removeAll(contextLookup.lookupAll(Node.class));
1042:
1043:                    if (!toAdd.isEmpty()) {
1044:                        contextLookup = new ProxyLookup(contextLookup, Lookups
1045:                                .fixed((Object[]) toAdd.toArray(new Node[toAdd
1046:                                        .size()])));
1047:                    }
1048:                    return contextLookup;
1049:                }
1050:            }
1051:
1052:            /** Returns the tree path nearby to given tree node. Either a sibling if there is or the parent.
1053:             * @param parentPath tree path to parent of changed nodes
1054:             * @param childIndices indexes of changed children
1055:             * @return the tree path or null if there no changed children
1056:             */
1057:            final static TreePath findSiblingTreePath(TreePath parentPath,
1058:                    int[] childIndices) {
1059:                if (childIndices == null) {
1060:                    throw new IllegalArgumentException(
1061:                            "Indexes of changed children are null."); // NOI18N
1062:                }
1063:
1064:                if (parentPath == null) {
1065:                    throw new IllegalArgumentException(
1066:                            "The tree path to parent is null."); // NOI18N
1067:                }
1068:
1069:                // bugfix #29342, if childIndices is the empty then don't change the selection
1070:                if (childIndices.length == 0) {
1071:                    return null;
1072:                }
1073:
1074:                TreeNode parent = (TreeNode) parentPath.getLastPathComponent();
1075:                Object[] parentPaths = parentPath.getPath();
1076:                TreePath newSelection = null;
1077:
1078:                int childCount = parent.getChildCount();
1079:                if (childCount > 0) {
1080:                    // get parent path, add child to it
1081:                    int childPathLength = parentPaths.length + 1;
1082:                    Object[] childPath = new Object[childPathLength];
1083:                    System.arraycopy(parentPaths, 0, childPath, 0,
1084:                            parentPaths.length);
1085:
1086:                    int selectedChild = Math.min(childIndices[0],
1087:                            childCount - 1);
1088:
1089:                    childPath[childPathLength - 1] = parent
1090:                            .getChildAt(selectedChild);
1091:                    newSelection = new TreePath(childPath);
1092:                } else {
1093:                    // all children removed, select parent
1094:                    newSelection = new TreePath(parentPaths);
1095:                }
1096:
1097:                return newSelection;
1098:            }
1099:
1100:            // Workaround for JDK issue 6472844 (NB #84970)
1101:            void removedNodes(List<VisualizerNode> removed) {
1102:                TreeSelectionModel sm = tree.getSelectionModel();
1103:                TreePath[] selPaths = (sm != null) ? sm.getSelectionPaths()
1104:                        : null;
1105:                if (selPaths == null)
1106:                    return;
1107:
1108:                List<TreePath> remSel = null;
1109:                for (VisualizerNode vn : removed) {
1110:                    TreePath path = new TreePath(treeModel.getPathToRoot(vn));
1111:                    for (TreePath tp : selPaths) {
1112:                        if (path.isDescendant(tp)) {
1113:                            if (remSel == null)
1114:                                remSel = new ArrayList();
1115:                            remSel.add(tp);
1116:                        }
1117:                    }
1118:                }
1119:
1120:                if (remSel != null) {
1121:                    sm.removeSelectionPaths(remSel.toArray(new TreePath[remSel
1122:                            .size()]));
1123:                }
1124:            }
1125:
1126:            private static class CursorR implements  Runnable {
1127:                private Container contentPane;
1128:                private Cursor c;
1129:
1130:                private CursorR(Container cont, Cursor c) {
1131:                    contentPane = cont;
1132:                    this .c = c;
1133:                }
1134:
1135:                public void run() {
1136:                    contentPane.setCursor(c);
1137:                }
1138:            }
1139:
1140:            /** Listens to the property changes on tree */
1141:            class TreePropertyListener implements  VetoableChangeListener,
1142:                    PropertyChangeListener, TreeExpansionListener,
1143:                    TreeWillExpandListener, TreeSelectionListener, Runnable {
1144:                private RequestProcessor.Task scheduled;
1145:                private TreePath[] readAccessPaths;
1146:
1147:                TreePropertyListener() {
1148:                }
1149:
1150:                public void vetoableChange(PropertyChangeEvent evt)
1151:                        throws PropertyVetoException {
1152:                    if (evt.getPropertyName().equals(
1153:                            ExplorerManager.PROP_SELECTED_NODES)) {
1154:                        // issue 11928 check if selecetion mode will be broken
1155:                        Node[] nodes = (Node[]) evt.getNewValue();
1156:
1157:                        if (isSelectionModeBroken(nodes)) {
1158:                            throw new PropertyVetoException("selection mode "
1159:                                    + getSelectionMode() + " broken by "
1160:                                    + Arrays.asList(nodes), evt); // NOI18N
1161:                        }
1162:
1163:                        if (!selectionAccept(nodes)) {
1164:                            throw new PropertyVetoException("selection "
1165:                                    + Arrays.asList(nodes) + " rejected", evt); // NOI18N
1166:                        }
1167:                    }
1168:                }
1169:
1170:                public final void propertyChange(final PropertyChangeEvent evt) {
1171:                    if (manager == null) {
1172:                        return; // the tree view has been removed before the event got delivered
1173:                    }
1174:
1175:                    if (evt.getPropertyName().equals(
1176:                            ExplorerManager.PROP_ROOT_CONTEXT)) {
1177:                        synchronizeRootContext();
1178:                    }
1179:
1180:                    if (evt.getPropertyName().equals(
1181:                            ExplorerManager.PROP_EXPLORED_CONTEXT)) {
1182:                        synchronizeExploredContext();
1183:                    }
1184:
1185:                    if (evt.getPropertyName().equals(
1186:                            ExplorerManager.PROP_SELECTED_NODES)) {
1187:                        synchronizeSelectedNodes();
1188:                    }
1189:                }
1190:
1191:                public synchronized void treeExpanded(TreeExpansionEvent ev) {
1192:
1193:                    if (!tree.getScrollsOnExpand()) {
1194:                        return;
1195:                    }
1196:
1197:                    RequestProcessor.Task t = scheduled;
1198:
1199:                    if (t != null) {
1200:                        t.cancel();
1201:                    }
1202:
1203:                    class Request implements  Runnable {
1204:                        private TreePath path;
1205:
1206:                        public Request(TreePath path) {
1207:                            this .path = path;
1208:                        }
1209:
1210:                        public void run() {
1211:                            if (!SwingUtilities.isEventDispatchThread()) {
1212:                                SwingUtilities.invokeLater(this );
1213:
1214:                                return;
1215:                            }
1216:
1217:                            try {
1218:                                if (!tree.isVisible(path)) {
1219:                                    // if the path is not visible - don't check the children
1220:                                    return;
1221:                                }
1222:
1223:                                if (treeModel == null) {
1224:                                    // no model, no action, no problem
1225:                                    return;
1226:                                }
1227:
1228:                                TreeNode myNode = (TreeNode) path
1229:                                        .getLastPathComponent();
1230:
1231:                                if (treeModel.getPathToRoot(myNode)[0] != treeModel
1232:                                        .getRoot()) {
1233:                                    // the way from the path no longer
1234:                                    // goes to the root, probably someone
1235:                                    // has removed the node on the way up
1236:                                    // System.out.println("different roots.");
1237:                                    return;
1238:                                }
1239:
1240:                                // show wait cursor
1241:                                //showWaitCursor ();
1242:                                int lastChildIndex = myNode.getChildCount() - 1;
1243:
1244:                                if (lastChildIndex >= 0) {
1245:                                    TreeNode lastChild = myNode
1246:                                            .getChildAt(lastChildIndex);
1247:
1248:                                    Rectangle base = tree.getVisibleRect();
1249:                                    Rectangle b1 = tree.getPathBounds(path);
1250:                                    Rectangle b2 = tree
1251:                                            .getPathBounds(new TreePath(
1252:                                                    treeModel
1253:                                                            .getPathToRoot(lastChild)));
1254:
1255:                                    if ((base != null) && (b1 != null)
1256:                                            && (b2 != null)) {
1257:                                        tree.scrollRectToVisible(new Rectangle(
1258:                                                base.x, b1.y, 1, b2.y - b1.y
1259:                                                        + b2.height));
1260:                                    }
1261:
1262:                                    //                        scrollTreeToVisible(path, lastChild);
1263:                                }
1264:                            } finally {
1265:                                path = null;
1266:                            }
1267:                        }
1268:                    }
1269:
1270:                    // It is OK to use multithreaded shared RP as the requests
1271:                    // will be serialized in event queue later
1272:                    scheduled = RequestProcessor.getDefault().post(
1273:                            new Request(ev.getPath()), 250); // hope that all children are there after this time
1274:                }
1275:
1276:                public synchronized void treeCollapsed(
1277:                        final TreeExpansionEvent ev) {
1278:                    showNormalCursor();
1279:                    class Request implements  Runnable {
1280:                        private TreePath path;
1281:
1282:                        public Request(TreePath path) {
1283:                            this .path = path;
1284:                        }
1285:
1286:                        public void run() {
1287:                            if (!SwingUtilities.isEventDispatchThread()) {
1288:                                SwingUtilities.invokeLater(this );
1289:
1290:                                return;
1291:                            }
1292:
1293:                            try {
1294:                                if (tree.isExpanded(path)) {
1295:                                    // the tree shows the path - do not collapse
1296:                                    // the tree
1297:                                    return;
1298:                                }
1299:
1300:                                if (!tree.isVisible(path)) {
1301:                                    // if the path is not visible do not collapse
1302:                                    // the tree
1303:                                    return;
1304:                                }
1305:
1306:                                if (treeModel == null) {
1307:                                    // no model, no action, no problem
1308:                                    return;
1309:                                }
1310:
1311:                                TreeNode myNode = (TreeNode) path
1312:                                        .getLastPathComponent();
1313:
1314:                                if (treeModel.getPathToRoot(myNode)[0] != treeModel
1315:                                        .getRoot()) {
1316:                                    // the way from the path no longer
1317:                                    // goes to the root, probably someone
1318:                                    // has removed the node on the way up
1319:                                    // System.out.println("different roots.");
1320:                                    return;
1321:                                }
1322:
1323:                                treeModel.nodeStructureChanged(myNode);
1324:                            } finally {
1325:                                this .path = null;
1326:                            }
1327:                        }
1328:                    }
1329:
1330:                    // It is OK to use multithreaded shared RP as the requests
1331:                    // will be serialized in event queue later
1332:                    // bugfix #37420, children of all collapsed folders will be throw out
1333:                    RequestProcessor.getDefault().post(
1334:                            new Request(ev.getPath()), TIME_TO_COLLAPSE);
1335:                }
1336:
1337:                /* Called whenever the value of the selection changes.
1338:                 * @param ev the event that characterizes the change.
1339:                 */
1340:                public void valueChanged(TreeSelectionEvent ev) {
1341:                    TreePath[] paths = tree.getSelectionPaths();
1342:                    storeSelectedPaths = Arrays
1343:                            .asList((paths == null) ? new TreePath[0] : paths);
1344:
1345:                    if (paths == null) {
1346:                        // part of bugfix #37279, if DnD is active then is useless select a nearby node
1347:                        if (ExplorerDnDManager.getDefault().isDnDActive()) {
1348:                            return;
1349:                        }
1350:
1351:                        callSelectionChanged(new Node[0]);
1352:                    } else {
1353:                        // we need to force no changes to nodes hierarchy =>
1354:                        // we are requesting read request, but it is not necessary
1355:                        // to execute the next action immediatelly, so postReadRequest
1356:                        // should be enough
1357:                        readAccessPaths = paths;
1358:                        Children.MUTEX.postReadRequest(this );
1359:                    }
1360:                }
1361:
1362:                /** Called under Children.MUTEX to refresh the currently selected nodes.
1363:                 */
1364:                public void run() {
1365:                    if (readAccessPaths == null) {
1366:                        return;
1367:                    }
1368:
1369:                    TreePath[] paths = readAccessPaths;
1370:
1371:                    // non null value caused leak in
1372:                    // ComponentInspector
1373:                    // When the last Form was closed then the ComponentInspector was
1374:                    // closed as well. Since this variable was not null - 
1375:                    // last selected Node (RADComponentNode) was held ---> FormManager2 was held, etc.
1376:                    readAccessPaths = null;
1377:
1378:                    java.util.List<Node> ll = new java.util.ArrayList<Node>(
1379:                            paths.length);
1380:
1381:                    for (int i = 0; i < paths.length; i++) {
1382:                        Node n = Visualizer.findNode(paths[i]
1383:                                .getLastPathComponent());
1384:
1385:                        if (isUnderRoot(manager.getRootContext(), n)) {
1386:                            ll.add(n);
1387:                        }
1388:                    }
1389:                    callSelectionChanged(ll.toArray(new Node[ll.size()]));
1390:                }
1391:
1392:                /** Checks whether given Node is a subnode of rootContext.
1393:                 * @return true if specified Node is under current rootContext
1394:                 */
1395:                private boolean isUnderRoot(Node rootContext, Node node) {
1396:                    while (node != null) {
1397:                        if (node.equals(rootContext)) {
1398:                            return true;
1399:                        }
1400:
1401:                        node = node.getParentNode();
1402:                    }
1403:
1404:                    return false;
1405:                }
1406:
1407:                public void treeWillCollapse(TreeExpansionEvent event)
1408:                        throws ExpandVetoException {
1409:                }
1410:
1411:                public void treeWillExpand(TreeExpansionEvent event)
1412:                        throws ExpandVetoException {
1413:                    // prepare wait cursor and optionally show it
1414:                    TreePath path = event.getPath();
1415:                    prepareWaitCursor(DragDropUtilities.secureFindNode(path
1416:                            .getLastPathComponent()));
1417:                }
1418:            }
1419:
1420:            // end of TreePropertyListener
1421:
1422:            /** Popup adapter.
1423:             */
1424:            class PopupAdapter extends MouseUtils.PopupMouseAdapter {
1425:                PopupAdapter() {
1426:                }
1427:
1428:                protected void showPopup(MouseEvent e) {
1429:                    int selRow = tree.getRowForLocation(e.getX(), e.getY());
1430:
1431:                    if ((selRow == -1) && !isRootVisible()) {
1432:                        // Use the invisible root node as a fake selection, and show its popup.
1433:                        try {
1434:                            manager.setSelectedNodes(new Node[] { manager
1435:                                    .getRootContext() });
1436:                        } catch (PropertyVetoException exc) {
1437:                            assert false : exc; // not permitted to be thrown
1438:                        }
1439:                    } else if (!tree.isRowSelected(selRow)) {
1440:                        // This will set ExplorerManager selection as well.
1441:                        // If selRow == -1 the selection will be cleared.
1442:                        tree.setSelectionRow(selRow);
1443:                    }
1444:
1445:                    if ((selRow != -1) || !isRootVisible()) {
1446:                        Point p = SwingUtilities.convertPoint(e.getComponent(),
1447:                                e.getX(), e.getY(), TreeView.this );
1448:
1449:                        createPopup((int) p.getX(), (int) p.getY());
1450:                    }
1451:                }
1452:            }
1453:
1454:            final class PopupSupport extends MouseAdapter implements  Runnable,
1455:                    FocusListener, ActionListener {
1456:                public final Action popup = new AbstractAction() {
1457:                    public void actionPerformed(ActionEvent evt) {
1458:                        SwingUtilities.invokeLater(PopupSupport.this );
1459:                    }
1460:
1461:                    /**
1462:                     * Returns true if the action is enabled.
1463:                     *
1464:                     * @return true if the action is enabled, false otherwise
1465:                     * @see Action#isEnabled
1466:                     */
1467:                    public boolean isEnabled() {
1468:                        return TreeView.this .isFocusOwner()
1469:                                || tree.isFocusOwner();
1470:                    }
1471:                };
1472:
1473:                //CallbackSystemAction csa;
1474:                public void run() {
1475:                    Point p = getPositionForPopup();
1476:
1477:                    if (p == null) {
1478:                        //we're going to create a popup menu for the root node
1479:                        p = new Point(0, 0);
1480:                    }
1481:
1482:                    createPopup(p.x, p.y);
1483:                }
1484:
1485:                public void focusGained(java.awt.event.FocusEvent ev) {
1486:                    // unregister
1487:                    ev.getComponent().removeFocusListener(this );
1488:
1489:                    // lazy activation of drag source
1490:                    if (DragDropUtilities.dragAndDropEnabled && dragActive) {
1491:                        setDragSource(true);
1492:
1493:                        // note: dropTarget is activated in constructor
1494:                    }
1495:                }
1496:
1497:                public void focusLost(FocusEvent ev) {
1498:                }
1499:
1500:                /* clicking adapter */
1501:                public void mouseClicked(MouseEvent e) {
1502:                    int selRow = tree.getRowForLocation(e.getX(), e.getY());
1503:
1504:                    if ((selRow != -1) && SwingUtilities.isLeftMouseButton(e)
1505:                            && MouseUtils.isDoubleClick(e)) {
1506:                        // Default action.
1507:                        if (defaultActionEnabled) {
1508:                            TreePath selPath = tree.getPathForLocation(
1509:                                    e.getX(), e.getY());
1510:                            Node node = Visualizer.findNode(selPath
1511:                                    .getLastPathComponent());
1512:
1513:                            Action a = takeAction(node.getPreferredAction(),
1514:                                    node);
1515:
1516:                            if (a != null) {
1517:                                if (a.isEnabled()) {
1518:                                    a.actionPerformed(new ActionEvent(node,
1519:                                            ActionEvent.ACTION_PERFORMED, "")); // NOI18N
1520:                                } else {
1521:                                    Toolkit.getDefaultToolkit().beep();
1522:                                }
1523:
1524:                                e.consume();
1525:
1526:                                return;
1527:                            }
1528:                        }
1529:
1530:                        if (tree.isExpanded(selRow)) {
1531:                            tree.collapseRow(selRow);
1532:                        } else {
1533:                            tree.expandRow(selRow);
1534:                        }
1535:                    }
1536:                }
1537:
1538:                /* VK_ENTER key processor */
1539:                public void actionPerformed(ActionEvent evt) {
1540:                    Node[] nodes = manager.getSelectedNodes();
1541:
1542:                    if (nodes.length > 0) {
1543:                        Action a = nodes[0].getPreferredAction();
1544:                        for (int i = 1; i < nodes.length; i++) {
1545:                            if (!nodes[i].getPreferredAction().equals(a))
1546:                                return;
1547:                        }
1548:
1549:                        // switch to replacement action if there is some
1550:                        a = takeAction(a, nodes);
1551:                        if (a != null && a.isEnabled()) {
1552:                            a.actionPerformed(new ActionEvent(
1553:                                    nodes.length == 1 ? nodes[0] : nodes,
1554:                                    ActionEvent.ACTION_PERFORMED, "")); // NOI18N
1555:                        } else {
1556:                            Toolkit.getDefaultToolkit().beep();
1557:                        }
1558:                    }
1559:                }
1560:            }
1561:
1562:            private final class ExplorerTree extends JTree implements 
1563:                    Autoscroll {
1564:                AutoscrollSupport support;
1565:                private String maxPrefix;
1566:                int SEARCH_FIELD_PREFERRED_SIZE = 160;
1567:                int SEARCH_FIELD_SPACE = 3;
1568:                private boolean firstPaint = true;
1569:
1570:                // searchTextField manages focus because it handles VK_TAB key
1571:                private JTextField searchTextField = new JTextField() {
1572:                    public boolean isManagingFocus() {
1573:                        return true;
1574:                    }
1575:
1576:                    public void processKeyEvent(KeyEvent ke) {
1577:                        //override the default handling so that
1578:                        //the parent will never receive the escape key and
1579:                        //close a modal dialog
1580:                        if (ke.getKeyCode() == ke.VK_ESCAPE) {
1581:                            removeSearchField();
1582:                            ke.consume();
1583:
1584:                            // bugfix #32909, reqest focus when search field is removed
1585:                            SwingUtilities.invokeLater(new Runnable() {
1586:                                //additional bugfix - do focus change later or removing
1587:                                //the component while it's focused will cause focus to
1588:                                //get transferred to the next component in the 
1589:                                //parent focusTraversalPolicy *after* our request
1590:                                //focus completes, so focus goes into a black hole - Tim
1591:                                public void run() {
1592:                                    ExplorerTree.this .requestFocus();
1593:                                }
1594:                            });
1595:                        } else {
1596:                            super .processKeyEvent(ke);
1597:                        }
1598:                    }
1599:                };
1600:
1601:                private JPanel searchpanel = null;
1602:                final private int heightOfTextField = searchTextField
1603:                        .getPreferredSize().height;
1604:                private int originalScrollMode;
1605:
1606:                ExplorerTree(TreeModel model) {
1607:                    super (model);
1608:                    toggleClickCount = 0;
1609:
1610:                    // fix for #18292
1611:                    // default action map for JTree defines these shortcuts
1612:                    // but we use our own mechanism for handling them
1613:                    // following lines disable default L&F handling (if it is
1614:                    // defined on Ctrl-c, Ctrl-v and Ctrl-x)
1615:                    getInputMap().put(KeyStroke.getKeyStroke("control C"),
1616:                            "none"); // NOI18N
1617:                    getInputMap().put(KeyStroke.getKeyStroke("control V"),
1618:                            "none"); // NOI18N
1619:                    getInputMap().put(KeyStroke.getKeyStroke("control X"),
1620:                            "none"); // NOI18N
1621:                    getInputMap().put(KeyStroke.getKeyStroke("COPY"), "none"); // NOI18N
1622:                    getInputMap().put(KeyStroke.getKeyStroke("PASTE"), "none"); // NOI18N
1623:                    getInputMap().put(KeyStroke.getKeyStroke("CUT"), "none"); // NOI18N
1624:
1625:                    if (Utilities.isMac()) {
1626:                        getInputMap().put(
1627:                                KeyStroke.getKeyStroke(KeyEvent.VK_C,
1628:                                        InputEvent.META_MASK), "none"); // NOI18N
1629:                        getInputMap().put(
1630:                                KeyStroke.getKeyStroke(KeyEvent.VK_X,
1631:                                        InputEvent.META_MASK), "none"); // NOI18N
1632:                        getInputMap().put(
1633:                                KeyStroke.getKeyStroke(KeyEvent.VK_V,
1634:                                        InputEvent.META_MASK), "none"); // NOI18N
1635:                    }
1636:
1637:                    setupSearch();
1638:
1639:                    setDragEnabled(true);
1640:                }
1641:
1642:                public void addNotify() {
1643:                    super .addNotify();
1644:                    ViewTooltips.register(this );
1645:                }
1646:
1647:                public void removeNotify() {
1648:                    super .removeNotify();
1649:                    ViewTooltips.unregister(this );
1650:                }
1651:
1652:                public void updateUI() {
1653:                    super .updateUI();
1654:                    setBorder(BorderFactory.createEmptyBorder());
1655:                    if (getTransferHandler() != null
1656:                            && getTransferHandler() instanceof  UIResource) {
1657:                        //we handle drag and drop in our own way, so let's just fool the UI with a dummy
1658:                        //TransferHandler to ensure that multiple selection is not lost when drag starts
1659:                        setTransferHandler(new DummyTransferHandler());
1660:                    }
1661:                }
1662:
1663:                private void calcRowHeight(Graphics g) {
1664:                    int height = Math.max(18, 2 + g.getFontMetrics(getFont())
1665:                            .getHeight());
1666:
1667:                    //Issue 42743/"Jesse mode"
1668:                    String s = System
1669:                            .getProperty("nb.cellrenderer.fixedheight"); //NOI18N
1670:
1671:                    if (s != null) {
1672:                        try {
1673:                            height = Integer.parseInt(s);
1674:                        } catch (Exception e) {
1675:                            //do nothing, height not changed
1676:                        }
1677:                    }
1678:
1679:                    if (getRowHeight() != height) {
1680:                        setRowHeight(height);
1681:                    } else {
1682:                        revalidate();
1683:                        repaint();
1684:                    }
1685:                }
1686:
1687:                //
1688:                // Certain operation should be executed in guarded mode - e.g.
1689:                // not allow changes in nodes during the operation being executed
1690:                //
1691:                public void paint(final Graphics g) {
1692:                    new GuardedActions(0, g);
1693:                }
1694:
1695:                protected void validateTree() {
1696:                    new GuardedActions(1, null);
1697:                }
1698:
1699:                public void doLayout() {
1700:                    new GuardedActions(2, null);
1701:                }
1702:
1703:                private void guardedPaint(Graphics g) {
1704:                    if (firstPaint) {
1705:                        firstPaint = false;
1706:                        calcRowHeight(g);
1707:
1708:                        //This will generate a repaint, so don't bother continuing with super.paint()
1709:                        //but do paint the background color so it doesn't paint gray the first time
1710:                        g.setColor(getBackground());
1711:                        g.fillRect(0, 0, getWidth(), getHeight());
1712:
1713:                        return;
1714:                    }
1715:
1716:                    ExplorerTree.super .paint(g);
1717:                }
1718:
1719:                private void guardedValidateTree() {
1720:                    super .validateTree();
1721:                }
1722:
1723:                private void guardedDoLayout() {
1724:                    super .doLayout();
1725:
1726:                    Rectangle visibleRect = getVisibleRect();
1727:
1728:                    if ((searchpanel != null) && searchpanel.isDisplayable()) {
1729:                        int width = Math.min(getPreferredSize().width
1730:                                - (SEARCH_FIELD_SPACE * 2),
1731:                                SEARCH_FIELD_PREFERRED_SIZE
1732:                                        - SEARCH_FIELD_SPACE);
1733:
1734:                        searchpanel
1735:                                .setBounds(Math.max(SEARCH_FIELD_SPACE,
1736:                                        (visibleRect.x + visibleRect.width)
1737:                                                - width), visibleRect.y
1738:                                        + SEARCH_FIELD_SPACE, Math.min(
1739:                                        visibleRect.width, width)
1740:                                        - SEARCH_FIELD_SPACE, heightOfTextField);
1741:                    }
1742:                }
1743:
1744:                public void setFont(Font f) {
1745:                    if (f != getFont()) {
1746:                        firstPaint = true;
1747:                        super .setFont(f);
1748:                    }
1749:                }
1750:
1751:                protected void processFocusEvent(FocusEvent fe) {
1752:                    super .processFocusEvent(fe);
1753:
1754:                    //Since the selected when focused is different, we need to force a
1755:                    //repaint of the entire selection, but let's do it in guarded more
1756:                    //as any other repaint
1757:                    new GuardedActions(3, null);
1758:                }
1759:
1760:                private void repaintSelection() {
1761:                    int first = getSelectionModel().getMinSelectionRow();
1762:                    int last = getSelectionModel().getMaxSelectionRow();
1763:
1764:                    if (first != -1) {
1765:                        if (first == last) {
1766:                            Rectangle r = getRowBounds(first);
1767:                            repaint(r.x, r.y, r.width, r.height);
1768:                        } else {
1769:                            Rectangle top = getRowBounds(first);
1770:                            Rectangle bottom = getRowBounds(last);
1771:                            Rectangle r = new Rectangle();
1772:                            r.x = Math.min(top.x, bottom.x);
1773:                            r.y = top.y;
1774:                            r.width = getWidth();
1775:                            r.height = (bottom.y + bottom.height) - top.y;
1776:                            repaint(r.x, r.y, r.width, r.height);
1777:                        }
1778:                    }
1779:                }
1780:
1781:                private void prepareSearchPanel() {
1782:                    if (searchpanel == null) {
1783:                        searchpanel = new JPanel();
1784:
1785:                        JLabel lbl = new JLabel(NbBundle.getMessage(
1786:                                TreeView.class, "LBL_QUICKSEARCH")); //NOI18N
1787:                        searchpanel.setLayout(new BoxLayout(searchpanel,
1788:                                BoxLayout.X_AXIS));
1789:                        searchpanel.add(lbl);
1790:                        searchpanel.add(searchTextField);
1791:                        lbl.setLabelFor(searchTextField);
1792:                        searchpanel.setBorder(BorderFactory
1793:                                .createRaisedBevelBorder());
1794:                        lbl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0,
1795:                                5));
1796:                    }
1797:                }
1798:
1799:                private void setupSearch() {
1800:                    // Remove the default key listeners
1801:                    KeyListener[] keyListeners = getListeners(KeyListener.class);
1802:
1803:                    for (int i = 0; i < keyListeners.length; i++) {
1804:                        removeKeyListener(keyListeners[i]);
1805:                    }
1806:
1807:                    // Add new key listeners
1808:                    addKeyListener(new KeyAdapter() {
1809:                        public void keyTyped(KeyEvent e) {
1810:                            int modifiers = e.getModifiers();
1811:                            int keyCode = e.getKeyCode();
1812:                            char c = e.getKeyChar();
1813:
1814:                            //#43617 - don't eat + and -
1815:                            //#98634 - and all its duplicates dont't react to space
1816:                            if ((c == '+') || (c == '-') || (c == ' '))
1817:                                return; // NOI18N
1818:
1819:                            if (((modifiers > 0) && (modifiers != KeyEvent.SHIFT_MASK))
1820:                                    || e.isActionKey()) {
1821:                                return;
1822:                            }
1823:
1824:                            if (Character.isISOControl(c)
1825:                                    || (keyCode == KeyEvent.VK_SHIFT)
1826:                                    || (keyCode == KeyEvent.VK_ESCAPE))
1827:                                return;
1828:
1829:                            final KeyStroke stroke = KeyStroke
1830:                                    .getKeyStrokeForEvent(e);
1831:                            searchTextField.setText(String.valueOf(stroke
1832:                                    .getKeyChar()));
1833:
1834:                            displaySearchField();
1835:                            e.consume();
1836:                        }
1837:                    });
1838:
1839:                    // Create a the "multi-event" listener for the text field. Instead of
1840:                    // adding separate instances of each needed listener, we're using a
1841:                    // class which implements them all. This approach is used in order 
1842:                    // to avoid the creation of 4 instances which takes some time
1843:                    SearchFieldListener searchFieldListener = new SearchFieldListener();
1844:                    searchTextField.addKeyListener(searchFieldListener);
1845:                    searchTextField.addFocusListener(searchFieldListener);
1846:                    searchTextField.getDocument().addDocumentListener(
1847:                            searchFieldListener);
1848:                }
1849:
1850:                private List<TreePath> doSearch(String prefix) {
1851:                    List<TreePath> results = new ArrayList<TreePath>();
1852:
1853:                    // do search forward the selected index
1854:                    int[] rows = getSelectionRows();
1855:                    int startIndex = ((rows == null) || (rows.length == 0)) ? 0
1856:                            : rows[0];
1857:
1858:                    int size = getRowCount();
1859:
1860:                    if (size == 0) {
1861:                        // Empty tree (no root visible); cannot match anything.
1862:                        return results;
1863:                    }
1864:
1865:                    while (true) {
1866:                        startIndex = startIndex % size;
1867:
1868:                        TreePath path = null;
1869:                        if (quickSearchUsingSubstring) {
1870:                            path = getNextSubstringMatch(prefix, startIndex,
1871:                                    Position.Bias.Forward);
1872:                        } else {
1873:                            path = getNextMatch(prefix, startIndex,
1874:                                    Position.Bias.Forward);
1875:                        }
1876:
1877:                        if ((path != null) && !results.contains(path)) {
1878:                            startIndex = tree.getRowForPath(path);
1879:                            results.add(path);
1880:
1881:                            if (!quickSearchUsingSubstring) {
1882:                                String elementName = ((VisualizerNode) path
1883:                                        .getLastPathComponent())
1884:                                        .getDisplayName();
1885:
1886:                                // initialize prefix
1887:                                if (maxPrefix == null) {
1888:                                    maxPrefix = elementName;
1889:                                }
1890:
1891:                                maxPrefix = findMaxPrefix(maxPrefix,
1892:                                        elementName);
1893:                            }
1894:                            // try next element
1895:                            startIndex++;
1896:                        } else {
1897:                            break;
1898:                        }
1899:                    }
1900:
1901:                    return results;
1902:                }
1903:
1904:                private String findMaxPrefix(String str1, String str2) {
1905:                    String res = null;
1906:
1907:                    for (int i = 0; str1.regionMatches(true, 0, str2, 0, i); i++) {
1908:                        res = str1.substring(0, i);
1909:                    }
1910:
1911:                    return res;
1912:                }
1913:
1914:                /**
1915:                 * Copied and adapted from JTree.getNextMatch(...).
1916:                 */
1917:                private TreePath getNextSubstringMatch(String substring,
1918:                        int startingRow, Position.Bias bias) {
1919:
1920:                    int max = getRowCount();
1921:                    if (substring == null) {
1922:                        throw new IllegalArgumentException();
1923:                    }
1924:                    if (startingRow < 0 || startingRow >= max) {
1925:                        throw new IllegalArgumentException();
1926:                    }
1927:                    substring = substring.toUpperCase();
1928:
1929:                    // start search from the next/previous element froom the 
1930:                    // selected element
1931:                    int increment = (bias == Position.Bias.Forward) ? 1 : -1;
1932:                    int row = startingRow;
1933:                    do {
1934:                        TreePath path = getPathForRow(row);
1935:                        String text = convertValueToText(path
1936:                                .getLastPathComponent(), isRowSelected(row),
1937:                                isExpanded(row), true, row, false);
1938:
1939:                        if (text.toUpperCase().indexOf(substring) >= 0) {
1940:                            return path;
1941:                        }
1942:                        row = (row + increment + max) % max;
1943:                    } while (row != startingRow);
1944:                    return null;
1945:                }
1946:
1947:                /**
1948:                 * Adds the search field to the tree.
1949:                 */
1950:                private void displaySearchField() {
1951:                    if (!searchTextField.isDisplayable()) {
1952:                        JViewport viewport = TreeView.this .getViewport();
1953:                        originalScrollMode = viewport.getScrollMode();
1954:                        viewport.setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
1955:                        searchTextField.setFont(ExplorerTree.this .getFont());
1956:                        prepareSearchPanel();
1957:                        add(searchpanel);
1958:                        revalidate();
1959:                        repaint();
1960:                        searchTextField.requestFocus();
1961:                    }
1962:                }
1963:
1964:                /**
1965:                 * Removes the search field from the tree.
1966:                 */
1967:                private void removeSearchField() {
1968:                    if (searchpanel.isDisplayable()) {
1969:                        remove(searchpanel);
1970:                        TreeView.this .getViewport().setScrollMode(
1971:                                originalScrollMode);
1972:
1973:                        Rectangle r = searchpanel.getBounds();
1974:                        this .repaint(r);
1975:                    }
1976:                }
1977:
1978:                /** notify the Component to autoscroll */
1979:                public void autoscroll(Point cursorLoc) {
1980:                    getSupport().autoscroll(cursorLoc);
1981:                }
1982:
1983:                /** @return the Insets describing the autoscrolling
1984:                 * region or border relative to the geometry of the
1985:                 * implementing Component.
1986:                 */
1987:                public Insets getAutoscrollInsets() {
1988:                    return getSupport().getAutoscrollInsets();
1989:                }
1990:
1991:                /** Safe getter for autoscroll support. */
1992:                AutoscrollSupport getSupport() {
1993:                    if (support == null) {
1994:                        support = new AutoscrollSupport(this , new Insets(15,
1995:                                10, 15, 10));
1996:                    }
1997:
1998:                    return support;
1999:                }
2000:
2001:                public String getToolTipText(MouseEvent event) {
2002:                    if (event != null) {
2003:                        Point p = event.getPoint();
2004:                        int selRow = getRowForLocation(p.x, p.y);
2005:
2006:                        if (selRow != -1) {
2007:                            TreePath path = getPathForRow(selRow);
2008:                            VisualizerNode v = (VisualizerNode) path
2009:                                    .getLastPathComponent();
2010:                            String tooltip = v.getShortDescription();
2011:                            String displayName = v.getDisplayName();
2012:
2013:                            if ((tooltip != null)
2014:                                    && !tooltip.equals(displayName)) {
2015:                                return tooltip;
2016:                            }
2017:                        }
2018:                    }
2019:
2020:                    return null;
2021:                }
2022:
2023:                protected TreeModelListener createTreeModelListener() {
2024:                    return new ModelHandler();
2025:                }
2026:
2027:                public AccessibleContext getAccessibleContext() {
2028:                    if (accessibleContext == null) {
2029:                        accessibleContext = new AccessibleExplorerTree();
2030:                    }
2031:
2032:                    return accessibleContext;
2033:                }
2034:
2035:                private class GuardedActions implements  Mutex.Action<Void> {
2036:                    private int type;
2037:                    private Object p1;
2038:
2039:                    public GuardedActions(int type, Object p1) {
2040:                        this .type = type;
2041:                        this .p1 = p1;
2042:                        Children.MUTEX.readAccess(this );
2043:                    }
2044:
2045:                    public Void run() {
2046:                        switch (type) {
2047:                        case 0:
2048:                            guardedPaint((Graphics) p1);
2049:
2050:                            break;
2051:
2052:                        case 1:
2053:                            guardedValidateTree();
2054:
2055:                            break;
2056:
2057:                        case 2:
2058:                            guardedDoLayout();
2059:
2060:                            break;
2061:
2062:                        case 3:
2063:                            repaintSelection();
2064:
2065:                            break;
2066:
2067:                        default:
2068:                            throw new IllegalStateException("type: " + type);
2069:                        }
2070:
2071:                        return null;
2072:                    }
2073:                }
2074:
2075:                private class SearchFieldListener extends KeyAdapter implements 
2076:                        DocumentListener, FocusListener {
2077:                    /** The last search results */
2078:                    private List<TreePath> results = new ArrayList<TreePath>();
2079:
2080:                    /** The last selected index from the search results. */
2081:                    private int currentSelectionIndex;
2082:
2083:                    SearchFieldListener() {
2084:                    }
2085:
2086:                    public void changedUpdate(DocumentEvent e) {
2087:                        searchForNode();
2088:                    }
2089:
2090:                    public void insertUpdate(DocumentEvent e) {
2091:                        searchForNode();
2092:                    }
2093:
2094:                    public void removeUpdate(DocumentEvent e) {
2095:                        searchForNode();
2096:                    }
2097:
2098:                    public void keyPressed(KeyEvent e) {
2099:                        int keyCode = e.getKeyCode();
2100:
2101:                        if (keyCode == KeyEvent.VK_ESCAPE) {
2102:                            removeSearchField();
2103:                            ExplorerTree.this .requestFocus();
2104:                        } else if (keyCode == KeyEvent.VK_UP) {
2105:                            currentSelectionIndex--;
2106:                            displaySearchResult();
2107:
2108:                            // Stop processing the event here. Otherwise it's dispatched
2109:                            // to the tree too (which scrolls)
2110:                            e.consume();
2111:                        } else if (keyCode == KeyEvent.VK_DOWN) {
2112:                            currentSelectionIndex++;
2113:                            displaySearchResult();
2114:
2115:                            // Stop processing the event here. Otherwise it's dispatched
2116:                            // to the tree too (which scrolls)
2117:                            e.consume();
2118:                        } else if (keyCode == KeyEvent.VK_TAB) {
2119:                            if (maxPrefix != null) {
2120:                                searchTextField.setText(maxPrefix);
2121:                            }
2122:
2123:                            e.consume();
2124:                        } else if (keyCode == KeyEvent.VK_ENTER) {
2125:                            removeSearchField();
2126:
2127:                            // bugfix #39607, don't expand selected node when default action invoked
2128:                            TreePath selectedTPath = getSelectionPath();
2129:
2130:                            if (selectedTPath != null) {
2131:                                TreeNode selectedTNode = (TreeNode) selectedTPath
2132:                                        .getLastPathComponent();
2133:                                Node selectedNode = Visualizer
2134:                                        .findNode(selectedTNode);
2135:
2136:                                if ((selectedNode.getPreferredAction() == null)
2137:                                        || !selectedNode.getPreferredAction()
2138:                                                .isEnabled()) {
2139:                                    expandPath(getSelectionPath());
2140:                                }
2141:                            }
2142:
2143:                            ExplorerTree.this .requestFocus();
2144:                            ExplorerTree.this .dispatchEvent(e);
2145:                        }
2146:                    }
2147:
2148:                    /** Searches for a node in the tree. */
2149:                    private void searchForNode() {
2150:                        currentSelectionIndex = 0;
2151:                        results.clear();
2152:                        maxPrefix = null;
2153:
2154:                        String text = searchTextField.getText().toUpperCase();
2155:
2156:                        if (text.length() > 0) {
2157:                            results = doSearch(text);
2158:                            displaySearchResult();
2159:                        }
2160:                    }
2161:
2162:                    private void displaySearchResult() {
2163:                        int sz = results.size();
2164:
2165:                        if (sz > 0) {
2166:                            if (currentSelectionIndex < 0) {
2167:                                currentSelectionIndex = sz - 1;
2168:                            } else if (currentSelectionIndex >= sz) {
2169:                                currentSelectionIndex = 0;
2170:                            }
2171:
2172:                            TreePath path = results.get(currentSelectionIndex);
2173:                            setSelectionPath(path);
2174:                            scrollPathToVisible(path);
2175:                        } else {
2176:                            clearSelection();
2177:                        }
2178:                    }
2179:
2180:                    public void focusGained(FocusEvent e) {
2181:                        // Do nothing
2182:                    }
2183:
2184:                    public void focusLost(FocusEvent e) {
2185:                        removeSearchField();
2186:                    }
2187:                }
2188:
2189:                private class AccessibleExplorerTree extends
2190:                        JTree.AccessibleJTree {
2191:                    AccessibleExplorerTree() {
2192:                    }
2193:
2194:                    public String getAccessibleName() {
2195:                        return TreeView.this .getAccessibleContext()
2196:                                .getAccessibleName();
2197:                    }
2198:
2199:                    public String getAccessibleDescription() {
2200:                        return TreeView.this .getAccessibleContext()
2201:                                .getAccessibleDescription();
2202:                    }
2203:                }
2204:
2205:                private class ModelHandler extends JTree.TreeModelHandler {
2206:                    ModelHandler() {
2207:                    }
2208:
2209:                    public void treeStructureChanged(TreeModelEvent e) {
2210:                        // Remember selections and expansions
2211:                        TreePath[] selectionPaths = getSelectionPaths();
2212:                        java.util.Enumeration expanded = getExpandedDescendants(e
2213:                                .getTreePath());
2214:
2215:                        // Restructure the node
2216:                        super .treeStructureChanged(e);
2217:
2218:                        // Expand previously expanded paths
2219:                        if (expanded != null) {
2220:                            while (expanded.hasMoreElements()) {
2221:                                expandPath((TreePath) expanded.nextElement());
2222:                            }
2223:                        }
2224:
2225:                        // Select previously selected paths
2226:                        if ((selectionPaths != null)
2227:                                && (selectionPaths.length > 0)) {
2228:                            boolean wasSelected = isPathSelected(selectionPaths[0]);
2229:
2230:                            setSelectionPaths(selectionPaths);
2231:
2232:                            if (!wasSelected) {
2233:                                // do not scroll if the first selection path survived structure change
2234:                                scrollPathToVisible(selectionPaths[0]);
2235:                            }
2236:                        }
2237:                    }
2238:
2239:                    public void treeNodesRemoved(TreeModelEvent e) {
2240:                        // called to removed from JTree.expandedState
2241:                        super .treeNodesRemoved(e);
2242:
2243:                        // part of bugfix #37279, if DnD is active then is useless select a nearby node
2244:                        if (ExplorerDnDManager.getDefault().isDnDActive()) {
2245:                            return;
2246:                        }
2247:
2248:                        if (tree.getSelectionCount() == 0) {
2249:                            TreePath path = findSiblingTreePath(
2250:                                    e.getTreePath(), e.getChildIndices());
2251:
2252:                            // bugfix #39564, don't select again the same object
2253:                            if ((path == null) || path.equals(e.getTreePath())) {
2254:                                return;
2255:                            } else if (path.getPathCount() > 0) {
2256:                                tree.setSelectionPath(path);
2257:                            }
2258:                        }
2259:                    }
2260:                }
2261:            }
2262:
2263:            private static class DummyTransferHandler extends TransferHandler /*implements UIResource*/{
2264:                public void exportAsDrag(JComponent comp, InputEvent e,
2265:                        int action) {
2266:                    //do nothing - ExplorerDnDManager will kick in when necessary
2267:                }
2268:
2269:                public void exportToClipboard(JComponent comp, Clipboard clip,
2270:                        int action) throws IllegalStateException {
2271:                    //do nothing - Node actions will hande this
2272:                }
2273:
2274:                public boolean canImport(JComponent comp,
2275:                        DataFlavor[] transferFlavors) {
2276:                    return false; //TreeViewDropSupport will decided
2277:                }
2278:
2279:                public boolean importData(JComponent comp, Transferable t) {
2280:                    return false;
2281:                }
2282:
2283:                public int getSourceActions(JComponent c) {
2284:                    return COPY_OR_MOVE;
2285:                }
2286:            }
2287:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.