KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > view > ListView


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.explorer.view;
21
22 import java.awt.Dimension JavaDoc;
23 import java.awt.Insets JavaDoc;
24 import java.awt.Point JavaDoc;
25 import java.awt.Rectangle JavaDoc;
26 import java.awt.Toolkit JavaDoc;
27 import java.awt.dnd.Autoscroll JavaDoc;
28 import java.awt.dnd.DnDConstants JavaDoc;
29 import java.awt.event.ActionEvent JavaDoc;
30 import java.awt.event.ActionListener JavaDoc;
31 import java.awt.event.FocusEvent JavaDoc;
32 import java.awt.event.FocusListener JavaDoc;
33 import java.awt.event.InputEvent JavaDoc;
34 import java.awt.event.KeyAdapter JavaDoc;
35 import java.awt.event.KeyEvent JavaDoc;
36 import java.awt.event.KeyListener JavaDoc;
37 import java.awt.event.MouseEvent JavaDoc;
38 import java.beans.PropertyChangeEvent JavaDoc;
39 import java.beans.PropertyChangeListener JavaDoc;
40 import java.beans.PropertyVetoException JavaDoc;
41 import java.beans.VetoableChangeListener JavaDoc;
42 import java.io.Externalizable JavaDoc;
43 import java.io.IOException JavaDoc;
44 import java.io.ObjectInput JavaDoc;
45 import java.io.ObjectOutput JavaDoc;
46 import java.util.ArrayList JavaDoc;
47 import java.util.List JavaDoc;
48 import javax.accessibility.AccessibleContext JavaDoc;
49 import javax.swing.AbstractAction JavaDoc;
50 import javax.swing.Action JavaDoc;
51 import javax.swing.BorderFactory JavaDoc;
52 import javax.swing.BoxLayout JavaDoc;
53 import javax.swing.JComponent JavaDoc;
54 import javax.swing.JLabel JavaDoc;
55 import javax.swing.JList JavaDoc;
56 import javax.swing.JPanel JavaDoc;
57 import javax.swing.JPopupMenu JavaDoc;
58 import javax.swing.JScrollPane JavaDoc;
59 import javax.swing.JTextField JavaDoc;
60 import javax.swing.JViewport JavaDoc;
61 import javax.swing.KeyStroke JavaDoc;
62 import javax.swing.ListSelectionModel JavaDoc;
63 import javax.swing.SwingUtilities JavaDoc;
64 import javax.swing.ToolTipManager JavaDoc;
65 import javax.swing.event.DocumentEvent JavaDoc;
66 import javax.swing.event.DocumentListener JavaDoc;
67 import javax.swing.event.ListDataEvent JavaDoc;
68 import javax.swing.event.ListDataListener JavaDoc;
69 import javax.swing.event.ListSelectionEvent JavaDoc;
70 import javax.swing.event.ListSelectionListener JavaDoc;
71 import javax.swing.text.Position JavaDoc;
72 import org.openide.awt.MouseUtils;
73 import org.openide.explorer.ExplorerManager;
74 import org.openide.explorer.ExplorerManager.Provider;
75 import org.openide.nodes.Node;
76 import org.openide.nodes.Node.Property;
77 import org.openide.nodes.NodeOp;
78 import org.openide.util.ContextAwareAction;
79 import org.openide.util.NbBundle;
80 import org.openide.util.Utilities;
81 import org.openide.util.WeakListeners;
82 import org.openide.util.actions.CallbackSystemAction;
83
84 /** Explorer view to display items in a list.
85  * <p>
86  * This class is a <q>view</q>
87  * to use it properly you need to add it into a component which implements
88  * {@link Provider}. Good examples of that can be found
89  * in {@link org.openide.explorer.ExplorerUtils}. Then just use
90  * {@link Provider#getExplorerManager} call to get the {@link ExplorerManager}
91  * and control its state.
92  * </p>
93  * <p>
94  * There can be multiple <q>views</q> under one container implementing {@link Provider}. Select from
95  * range of predefined ones or write your own:
96  * </p>
97  * <ul>
98  * <li>{@link org.openide.explorer.view.BeanTreeView} - shows a tree of nodes</li>
99  * <li>{@link org.openide.explorer.view.ContextTreeView} - shows a tree of nodes without leaf nodes</li>
100  * <li>{@link org.openide.explorer.view.ListView} - shows a list of nodes</li>
101  * <li>{@link org.openide.explorer.view.IconView} - shows a rows of nodes with bigger icons</li>
102  * <li>{@link org.openide.explorer.view.ChoiceView} - creates a combo box based on the explored nodes</li>
103  * <li>{@link org.openide.explorer.view.TreeTableView} - shows tree of nodes together with a set of their {@link Property}</li>
104  * <li>{@link org.openide.explorer.view.MenuView} - can create a {@link javax.swing.JMenu} structure based on structure of {@link Node}s</li>
105  * </ul>
106  * <p>
107  * All of these views use {@link ExplorerManager#find} to walk up the AWT hierarchy and locate the
108  * {@link ExplorerManager} to use as a controler. They attach as listeners to
109  * it and also call its setter methods to update the shared state based on the
110  * user action. Not all views make sence together, but for example
111  * {@link org.openide.explorer.view.ContextTreeView} and {@link org.openide.explorer.view.ListView} were designed to complement
112  * themselves and behaves like windows explorer. The {@link org.openide.explorer.propertysheet.PropertySheetView}
113  * for example should be able to work with any other view.
114  * </p>
115  * @author Ian Formanek, Jan Jancura, Jaroslav Tulach
116  */

117 public class ListView extends JScrollPane JavaDoc implements Externalizable JavaDoc {
118     /** generated Serialized Version UID */
119     static final long serialVersionUID = -7540940974042262975L;
120
121     /** Explorer manager to work with. Is not null only if the component is showing
122     * in components hierarchy
123     */

124     private transient ExplorerManager manager;
125
126     /** The actual JList list */
127     transient protected JList JavaDoc list;
128
129     /** model to use */
130     transient protected NodeListModel model;
131
132     //
133
// listeners
134
//
135

136     /** Listener to nearly everything */
137     transient Listener JavaDoc managerListener;
138
139     /** weak variation of the listener for property change on the explorer manager */
140     transient PropertyChangeListener JavaDoc wlpc;
141
142     /** weak variation of the listener for vetoable change on the explorer manager */
143     transient VetoableChangeListener JavaDoc wlvc;
144
145     /** popup */
146     transient PopupSupport popupSupport;
147
148     //
149
// properties
150
//
151

152     /** if true, the icon view displays a popup on right mouse click, if false, the popup is not displayed */
153     private boolean popupAllowed = true;
154
155     /** if true, the hierarchy traversal is allowed, if false, it is disabled */
156     private boolean traversalAllowed = true;
157
158     /** action preformer */
159     private ActionListener JavaDoc defaultProcessor;
160
161     //
162
// Dnd
163
//
164

165     /** true if drag support is active */
166     transient boolean dragActive = false;
167
168     /** true if drop support is active */
169     transient boolean dropActive = false;
170
171     /** Drag support */
172     transient ListViewDragSupport dragSupport;
173
174     /** Drop support */
175     transient ListViewDropSupport dropSupport;
176
177     // default DnD actions
178
transient private int allowedDragActions = DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE;
179     transient private int allowedDropActions = DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE;
180
181     /** True, if the selection listener is attached. */
182     transient boolean listenerActive;
183
184     // init .................................................................................
185

186     /** Default constructor.
187     */

188     public ListView() {
189         initializeList();
190
191         // activation of drop target
192
setDropTarget( DragDropUtilities.dragAndDropEnabled );
193
194         // no border, window system manages outer border itself
195
setBorder(BorderFactory.createEmptyBorder());
196         setViewportBorder(BorderFactory.createEmptyBorder());
197     }
198
199     /** Initializes the tree & model.
200     */

201     private void initializeList() {
202         // initilizes the JTree
203
model = createModel();
204         list = createList();
205         list.setModel(model);
206
207         setViewportView(list);
208
209         {
210             AbstractAction JavaDoc action = new GoUpAction();
211             KeyStroke JavaDoc key = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0);
212             list.registerKeyboardAction(action, key, JComponent.WHEN_FOCUSED);
213         }
214
215         {
216             AbstractAction JavaDoc action = new EnterAction();
217             KeyStroke JavaDoc key = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
218             list.registerKeyboardAction(action, key, JComponent.WHEN_FOCUSED);
219         }
220
221         managerListener = new Listener JavaDoc();
222         popupSupport = new PopupSupport();
223         list.getActionMap().put("org.openide.actions.PopupAction", popupSupport); // NOI18N
224

225         list.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
226
227         ToolTipManager.sharedInstance().registerComponent(list);
228     }
229
230     /*
231     * Write view's state to output stream.
232     */

233     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
234         out.writeObject(popupAllowed ? Boolean.TRUE : Boolean.FALSE);
235         out.writeObject(traversalAllowed ? Boolean.TRUE : Boolean.FALSE);
236         out.writeObject(new Integer JavaDoc(getSelectionMode()));
237     }
238
239     /*
240     * Reads view's state form output stream.
241     */

242     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
243         popupAllowed = ((Boolean JavaDoc) in.readObject()).booleanValue();
244         traversalAllowed = ((Boolean JavaDoc) in.readObject()).booleanValue();
245         setSelectionMode(((Integer JavaDoc) in.readObject()).intValue());
246     }
247
248     // properties ...........................................................................
249

250     /** Test whether display of a popup menu is enabled.
251      * @return <code>true</code> if so */

252     public boolean isPopupAllowed() {
253         return popupAllowed;
254     }
255
256     /** Enable/disable displaying popup menus on list view items. Default is enabled.
257     * @param value <code>true</code> to enable
258     */

259     public void setPopupAllowed(boolean value) {
260         popupAllowed = value;
261     }
262
263     /** Test whether hierarchy traversal shortcuts are permitted.
264     * @return <code>true</code> if so */

265     public boolean isTraversalAllowed() {
266         return traversalAllowed;
267     }
268
269     /** Enable/disable hierarchy traversal using <code>CTRL+click</code> (down) and <code>Backspace</code> (up), default is enabled.
270     * @param value <code>true</code> to enable
271     */

272     public void setTraversalAllowed(boolean value) {
273         traversalAllowed = value;
274     }
275
276     /** Get the current processor for default actions.
277     * If not <code>null</code>, double-clicks or pressing Enter on
278     * items in the view will not perform the default action on the selected node; rather the processor
279     * will be notified about the event.
280     * @return the current default-action processor, or <code>null</code>
281     */

282     public ActionListener JavaDoc getDefaultProcessor() {
283         return defaultProcessor;
284     }
285
286     /** Set a new processor for default actions.
287     * @param value the new default-action processor, or <code>null</code> to restore use of the selected node's declared default action
288     * @see #getDefaultProcessor
289     */

290     public void setDefaultProcessor(ActionListener JavaDoc value) {
291         defaultProcessor = value;
292     }
293
294     /**
295      * Set whether single-item or multiple-item
296      * selections are allowed.
297      * @param selectionMode one of {@link ListSelectionModel#SINGLE_SELECTION}, {@link ListSelectionModel#SINGLE_INTERVAL_SELECTION}, or {@link ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}
298      * @see ListSelectionModel#setSelectionMode
299      */

300     public void setSelectionMode(int selectionMode) {
301         list.setSelectionMode(selectionMode);
302     }
303
304     /** Get the selection mode.
305      * @return the mode
306      * @see #setSelectionMode
307      */

308     public int getSelectionMode() {
309         return list.getSelectionMode();
310     }
311
312     /********** Support for the Drag & Drop operations *********/
313     /** @return true if dragging from the view is enabled, false
314     * otherwise.<br>
315     * Drag support is disabled by default.
316     */

317     public boolean isDragSource() {
318         return dragActive;
319     }
320
321     /** Enables/disables dragging support.
322     * @param state true enables dragging support, false disables it.
323     */

324     public void setDragSource(boolean state) {
325         if (state == dragActive) {
326             return;
327         }
328
329         dragActive = state;
330
331         // create drag support if needed
332
if (dragActive && (dragSupport == null)) {
333             dragSupport = new ListViewDragSupport(this, list);
334         }
335
336         // activate / deactivate support according to the state
337
dragSupport.activate(dragActive);
338     }
339
340     /** @return true if dropping to the view is enabled, false
341     * otherwise<br>
342     * Drop support is disabled by default.
343     */

344     public boolean isDropTarget() {
345         return dropActive;
346     }
347
348     /** Enables/disables dropping support.
349     * @param state true means drops into view are allowed,
350     * false forbids any drops into this view.
351     */

352     public void setDropTarget(boolean state) {
353         if (state == dropActive) {
354             return;
355         }
356
357         dropActive = state;
358
359         // create drop support if needed
360
if (dropActive && (dropSupport == null)) {
361             dropSupport = new ListViewDropSupport(this, list);
362         }
363
364         // activate / deactivate support according to the state
365
dropSupport.activate(dropActive);
366     }
367
368     /** Actions constants comes from {@link java.awt.dnd.DnDConstants}.
369     * All actions (copy, move, link) are allowed by default.
370     * @return int representing set of actions which are allowed when dragging from
371     * asociated component.
372      */

373     public int getAllowedDragActions() {
374         return allowedDragActions;
375     }
376
377     /** Sets allowed actions for dragging
378     * @param actions new drag actions, using {@link java.awt.dnd.DnDConstants}
379     */

380     public void setAllowedDragActions(int actions) {
381         // PENDING check parameters
382
allowedDragActions = actions;
383     }
384
385     /** Actions constants comes from {@link java.awt.dnd.DnDConstants}.
386     * All actions are allowed by default.
387     * @return int representing set of actions which are allowed when dropping
388     * into the asociated component.
389     */

390     public int getAllowedDropActions() {
391         return allowedDropActions;
392     }
393
394     /** Sets allowed actions for dropping.
395     * @param actions new allowed drop actions, using {@link java.awt.dnd.DnDConstants}
396     */

397     public void setAllowedDropActions(int actions) {
398         // PENDING check parameters
399
allowedDropActions = actions;
400     }
401
402     //
403
// Methods to override
404
//
405

406     /** Creates the list that will display the data.
407     */

408     protected JList JavaDoc createList() {
409         JList JavaDoc list = new NbList();
410         list.setCellRenderer(new NodeRenderer());
411
412         return list;
413     }
414
415     /** Allows subclasses to change the default model used for
416     * the list.
417     */

418     protected NodeListModel createModel() {
419         return new NodeListModel();
420     }
421
422     /** Called when the list changed selection and the explorer manager
423     * should be updated.
424     * @param nodes list of nodes that should be selected
425     * @param em explorer manager
426     * @exception PropertyVetoException if the manager does not allow the
427     * selection
428     */

429     protected void selectionChanged(Node[] nodes, ExplorerManager em)
430     throws PropertyVetoException JavaDoc {
431         em.setSelectedNodes(nodes);
432     }
433
434     /** Called when explorer manager is about to change the current selection.
435     * The view can forbid the change if it is not able to display such
436     * selection.
437     *
438     * @param nodes the nodes to select
439     * @return false if the view is not able to change the selection
440     */

441     protected boolean selectionAccept(Node[] nodes) {
442         // if the selection is just the root context, confirm the selection
443
if ((nodes.length == 1) && manager.getRootContext().equals(nodes[0])) {
444             // XXX shouldn't this be exploredContext?
445
return true;
446         }
447
448         // we do not allow selection in other than the exploredContext
449
for (int i = 0; i < nodes.length; i++) {
450             VisualizerNode v = VisualizerNode.getVisualizer(null, nodes[i]);
451
452             if (model.getIndex(v) == -1) {
453                 return false;
454             }
455         }
456
457         return true;
458     }
459
460     /** Shows selection.
461     * @param indexes indexes of objects to select
462     */

463     protected void showSelection(int[] indexes) {
464         list.setSelectedIndices(indexes);
465     }
466
467     //
468
// Working methods
469
//
470

471     /* Initilizes the view.
472     */

473     public void addNotify() {
474         super.addNotify();
475
476         // run under mutex
477
ExplorerManager em = ExplorerManager.find(this);
478
479         if (em != manager) {
480             if (manager != null) {
481                 manager.removeVetoableChangeListener(wlvc);
482                 manager.removePropertyChangeListener(wlpc);
483             }
484
485             manager = em;
486
487             manager.addVetoableChangeListener(wlvc = WeakListeners.vetoableChange(managerListener, manager));
488             manager.addPropertyChangeListener(wlpc = WeakListeners.propertyChange(managerListener, manager));
489
490             model.setNode(manager.getExploredContext());
491
492             updateSelection();
493         } else {
494             // bugfix #23509, the listener were removed --> add it again
495
if (!listenerActive && (manager != null)) {
496                 manager.addVetoableChangeListener(wlvc = WeakListeners.vetoableChange(managerListener, manager));
497                 manager.addPropertyChangeListener(wlpc = WeakListeners.propertyChange(managerListener, manager));
498             }
499         }
500
501         if (!listenerActive) {
502             listenerActive = true;
503             list.getSelectionModel().addListSelectionListener(managerListener);
504             model.addListDataListener(managerListener);
505
506             // bugfix #23974, model doesn't reflect an explorer context change
507
// because any listener was not active
508
model.setNode(manager.getExploredContext());
509             list.addMouseListener(popupSupport);
510         }
511     }
512
513     /** Removes listeners.
514     */

515     public void removeNotify() {
516         super.removeNotify();
517         listenerActive = false;
518         list.getSelectionModel().removeListSelectionListener(managerListener);
519
520         // bugfix #23509, remove useless listeners
521
if (manager != null) {
522             manager.removeVetoableChangeListener(wlvc);
523             manager.removePropertyChangeListener(wlpc);
524         }
525
526         model.removeListDataListener(managerListener);
527         list.removeMouseListener(popupSupport);
528     }
529
530     /* Requests focus for the list component. Overrides superclass method. */
531     public void requestFocus() {
532         list.requestFocus();
533     }
534
535     /* Requests focus for the list component. Overrides superclass method. */
536     public boolean requestFocusInWindow() {
537         return list.requestFocusInWindow();
538     }
539
540     /** This method is called when user double-clicks on some object or
541     * presses Enter key.
542     * @param index Index of object in current explored context
543     */

544     final void performObjectAt(int index, int modifiers) {
545         if ((index < 0) || (index >= model.getSize())) {
546             return;
547         }
548
549         VisualizerNode v = (VisualizerNode) model.getElementAt(index);
550         Node node = v.node;
551
552         // if DefaultProcessor is set, the default action is notified to it overriding the default action on nodes
553
if (defaultProcessor != null) {
554             defaultProcessor.actionPerformed(new ActionEvent JavaDoc(node, 0, null, modifiers));
555
556             return;
557         }
558
559         // on double click - invoke default action, if there is any
560
// (unless user holds CTRL key what means that we should always dive into the context)
561
Action JavaDoc a = node.getPreferredAction();
562
563         if ((a != null) && ((modifiers & InputEvent.CTRL_MASK) == 0)) {
564             if (a instanceof ContextAwareAction) {
565                 a = ((ContextAwareAction) a).createContextAwareInstance(node.getLookup());
566             }
567
568             if (a.isEnabled()) {
569                 a.actionPerformed(new ActionEvent JavaDoc(node, ActionEvent.ACTION_PERFORMED, "")); // NOI18N
570
} else {
571                 Toolkit.getDefaultToolkit().beep();
572             }
573         }
574         // otherwise dive into the context
575
else if (traversalAllowed && (!node.isLeaf())) {
576             manager.setExploredContext(node, manager.getSelectedNodes());
577         }
578     }
579
580     /** Called when selection has been changed. Make selection visible (at least partly).
581     */

582     private void updateSelection() {
583         Node[] sel = manager.getSelectedNodes();
584         int[] indices = new int[sel.length];
585
586         // bugfix #27094, make sure a selection is visible
587
int firstVisible = list.getFirstVisibleIndex();
588         int lastVisible = list.getLastVisibleIndex();
589         boolean ensureVisible = indices.length > 0;
590
591         for (int i = 0; i < sel.length; i++) {
592             VisualizerNode v = VisualizerNode.getVisualizer(null, sel[i]);
593             indices[i] = model.getIndex(v);
594             ensureVisible = ensureVisible && ((indices[i] < firstVisible) || (indices[i] > lastVisible));
595         }
596
597         // going to change list because of E.M.'s order -- temp disable the
598
// listener
599
if (listenerActive) {
600             list.getSelectionModel().removeListSelectionListener(managerListener);
601         }
602
603         try {
604             showSelection(indices);
605
606             if (ensureVisible) {
607                 list.ensureIndexIsVisible(indices[0]);
608             }
609         } finally {
610             if (listenerActive) {
611                 list.getSelectionModel().addListSelectionListener(managerListener);
612             }
613         }
614     }
615
616     void createPopup(int xpos, int ypos, boolean contextMenu) {
617         if (manager == null) {
618             return;
619         }
620
621         if (!popupAllowed) {
622             return;
623         }
624
625         JPopupMenu JavaDoc popup;
626
627         if (contextMenu) {
628             popup = Utilities.actionsToPopup(manager.getExploredContext().getActions(true), this);
629         } else {
630             Action JavaDoc[] actions = NodeOp.findActions(manager.getSelectedNodes());
631             popup = Utilities.actionsToPopup(actions, this);
632         }
633
634         if ((popup != null) && (popup.getSubElements().length > 0)) {
635             Point JavaDoc p = getViewport().getViewPosition();
636             p.x = xpos - p.x;
637             p.y = ypos - p.y;
638
639             SwingUtilities.convertPointToScreen(p, ListView.this);
640
641             Dimension JavaDoc popupSize = popup.getPreferredSize();
642             Rectangle JavaDoc screenBounds = Utilities.getUsableScreenBounds(getGraphicsConfiguration());
643
644             if ((p.x + popupSize.width) > (screenBounds.x + screenBounds.width)) {
645                 p.x = (screenBounds.x + screenBounds.width) - popupSize.width;
646             }
647
648             if ((p.y + popupSize.height) > (screenBounds.y + screenBounds.height)) {
649                 p.y = (screenBounds.y + screenBounds.height) - popupSize.height;
650             }
651
652             SwingUtilities.convertPointFromScreen(p, ListView.this);
653             popup.show(this, p.x, p.y);
654         }
655     }
656
657     // innerclasses .........................................................................
658

659     /**
660      * Enhancement of standard JList.
661      * Provides access to the Node's ToolTips, Accessibility and Autoscrolling.
662      */

663     final class NbList extends JList JavaDoc implements Autoscroll JavaDoc {
664         static final long serialVersionUID = -7571829536335024077L;
665
666         /** The worker for the scrolling */
667         AutoscrollSupport support;
668
669         // navigator
670
int SEARCH_FIELD_PREFERRED_SIZE = 160;
671         int SEARCH_FIELD_SPACE = 3;
672         private String JavaDoc maxPrefix;
673
674         // searchTextField manages focus because it handles VK_TAB key
675
private JTextField JavaDoc searchTextField = new JTextField JavaDoc() {
676                 public boolean isManagingFocus() {
677                     return true;
678                 }
679
680                 public void processKeyEvent(KeyEvent JavaDoc ke) {
681                     //override the default handling so that
682
//the parent will never receive the escape key and
683
//close a modal dialog
684
if (ke.getKeyCode() == ke.VK_ESCAPE) {
685                         removeSearchField();
686                         ke.consume(); // #44394
687

688                         // bugfix #32909, reqest focus when search field is removed
689
NbList.this.requestFocus();
690                     } else {
691                         super.processKeyEvent(ke);
692                     }
693                 }
694             };
695
696         final private int heightOfTextField = searchTextField.getPreferredSize().height;
697         private int originalScrollMode;
698         private JPanel JavaDoc searchpanel = null;
699
700         NbList() {
701             super();
702
703             // fix for #18292
704
// default action map for JList defines these shortcuts
705
// but we use our own mechanism for handling them
706
// following lines disable default L&F handling (if it is
707
// defined on Ctrl-c, Ctrl-v and Ctrl-x)
708
getInputMap().put(KeyStroke.getKeyStroke("control C"), "none"); // NOI18N
709
getInputMap().put(KeyStroke.getKeyStroke("control V"), "none"); // NOI18N
710
getInputMap().put(KeyStroke.getKeyStroke("control X"), "none"); // NOI18N
711
setupSearch();
712         }
713
714         public void addNotify() {
715             super.addNotify();
716             ViewTooltips.register(this);
717         }
718         
719         public void removeNotify() {
720             super.removeNotify();
721             ViewTooltips.unregister(this);
722         }
723
724         protected void processFocusEvent(FocusEvent JavaDoc fe) {
725             super.processFocusEvent(fe);
726             repaintSelection();
727         }
728
729         private void repaintSelection() {
730             int[] idx = getSelectedIndices();
731
732             if (idx.length == 0) {
733                 return;
734             }
735
736             for (int i = 0; i < idx.length; i++) {
737                 Rectangle JavaDoc r = getCellBounds(idx[i], idx[i]);
738                 repaint(r.x, r.y, r.width, r.height);
739             }
740         }
741
742         // ToolTips:
743

744         /**
745          * Overrides JComponent's getToolTipText method in order to allow
746          * Node's tips to be used if they are useful.
747          *
748          * @param event the MouseEvent that initiated the ToolTip display
749          */

750         public String JavaDoc getToolTipText(MouseEvent JavaDoc event) {
751             if (event != null) {
752                 Point JavaDoc p = event.getPoint();
753                 int row = locationToIndex(p);
754
755                 if (row >= 0) {
756                     VisualizerNode v = (VisualizerNode) model.getElementAt(row);
757                     String JavaDoc tooltip = v.getShortDescription();
758                     String JavaDoc displayName = v.getDisplayName();
759
760                     if ((tooltip != null) && !tooltip.equals(displayName)) {
761                         return tooltip;
762                     }
763                 }
764             }
765
766             return null;
767         }
768
769         // Autoscroll:
770

771         /** notify the Component to autoscroll */
772         public void autoscroll(Point JavaDoc cursorLoc) {
773             getSupport().autoscroll(cursorLoc);
774         }
775
776         /** @return the Insets describing the autoscrolling region or border
777          * relative to the geometry of the implementing Component.
778          */

779         public Insets JavaDoc getAutoscrollInsets() {
780             return getSupport().getAutoscrollInsets();
781         }
782
783         /** Safe getter for autoscroll support. */
784         AutoscrollSupport getSupport() {
785             if (support == null) {
786                 support = new AutoscrollSupport(this, new Insets JavaDoc(15, 10, 15, 10));
787             }
788
789             return support;
790         }
791
792         // Accessibility:
793
public AccessibleContext JavaDoc getAccessibleContext() {
794             if (accessibleContext == null) {
795                 accessibleContext = new AccessibleExplorerList();
796             }
797
798             return accessibleContext;
799         }
800
801         private void setupSearch() {
802             // Remove the default key listeners
803
KeyListener JavaDoc[] keyListeners = (KeyListener JavaDoc[]) (getListeners(KeyListener JavaDoc.class));
804
805             for (int i = 0; i < keyListeners.length; i++) {
806                 removeKeyListener(keyListeners[i]);
807             }
808
809             // Add new key listeners
810
addKeyListener(
811                 new KeyAdapter JavaDoc() {
812                     public void keyPressed(KeyEvent JavaDoc e) {
813                         int modifiers = e.getModifiers();
814                         int keyCode = e.getKeyCode();
815
816                         if (((modifiers > 0) && (modifiers != KeyEvent.SHIFT_MASK)) || e.isActionKey()) {
817                             return;
818                         }
819
820                         char c = e.getKeyChar();
821
822                         if (!Character.isISOControl(c) && (keyCode != KeyEvent.VK_SHIFT)) {
823                             searchTextField.setText(String.valueOf(c));
824                             displaySearchField();
825                         }
826                     }
827                 }
828             );
829
830             // Create a the "multi-event" listener for the text field. Instead of
831
// adding separate instances of each needed listener, we're using a
832
// class which implements them all. This approach is used in order
833
// to avoid the creation of 4 instances which takes some time
834
SearchFieldListener searchFieldListener = new SearchFieldListener();
835             searchTextField.addKeyListener(searchFieldListener);
836             searchTextField.addFocusListener(searchFieldListener);
837             searchTextField.getDocument().addDocumentListener(searchFieldListener);
838         }
839
840         private List JavaDoc<Integer JavaDoc> doSearch(String JavaDoc prefix) {
841             List JavaDoc<Integer JavaDoc> results = new ArrayList JavaDoc<Integer JavaDoc>();
842
843             // do search forward the selected index
844
int startIndex = (getSelectedIndex() == -1) ? 0 : getSelectedIndex();
845             int size = getModel().getSize();
846             if (size == 0) return results; // nothing to search
847

848             while (true) {
849                 startIndex = startIndex % size;
850                 startIndex = getNextMatch(prefix, startIndex, Position.Bias.Forward);
851
852                 if ((startIndex != -1) && !results.contains(new Integer JavaDoc(startIndex))) {
853                     results.add(Integer.valueOf(startIndex));
854
855                     String JavaDoc elementName = getModel().getElementAt(startIndex).toString();
856
857                     // initialize prefix
858
if (maxPrefix == null) {
859                         maxPrefix = elementName;
860                     }
861
862                     maxPrefix = findMaxPrefix(maxPrefix, elementName);
863
864                     // try next element
865
startIndex++;
866                 } else {
867                     break;
868                 }
869             }
870
871             return results;
872         }
873
874         private String JavaDoc findMaxPrefix(String JavaDoc str1, String JavaDoc str2) {
875             String JavaDoc res = null;
876
877             for (int i = 0; str1.regionMatches(true, 0, str2, 0, i); i++) {
878                 res = str1.substring(0, i);
879             }
880
881             return res;
882         }
883
884         private void prepareSearchPanel() {
885             if (searchpanel == null) {
886                 searchpanel = new JPanel JavaDoc();
887
888                 JLabel JavaDoc lbl = new JLabel JavaDoc(NbBundle.getMessage(TreeView.class, "LBL_QUICKSEARCH")); //NOI18N
889
searchpanel.setLayout(new BoxLayout JavaDoc(searchpanel, BoxLayout.X_AXIS));
890                 searchpanel.add(lbl);
891                 searchpanel.add(searchTextField);
892                 lbl.setLabelFor(searchTextField);
893                 searchpanel.setBorder(BorderFactory.createRaisedBevelBorder());
894                 lbl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
895             }
896         }
897
898         /**
899          * Adds the search field to the tree.
900          */

901         private void displaySearchField() {
902             if ((getModel().getSize() > 0) && !searchTextField.isDisplayable()) {
903                 JViewport JavaDoc viewport = ListView.this.getViewport();
904                 originalScrollMode = viewport.getScrollMode();
905                 viewport.setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
906
907                 //Rectangle visibleTreeRect = getVisibleRect();
908
prepareSearchPanel();
909                 add(searchpanel);
910                 revalidate();
911                 repaint();
912
913                 // bugfix #28501, avoid the chars duplicated on jdk1.3
914
SwingUtilities.invokeLater(
915                     new Runnable JavaDoc() {
916                         public void run() {
917                             searchTextField.requestFocus();
918                         }
919                     }
920                 );
921             }
922         }
923
924         public void doLayout() {
925             super.doLayout();
926
927             if ((searchpanel != null) && searchpanel.isDisplayable()) {
928                 Rectangle JavaDoc visibleRect = getVisibleRect();
929                 int width = Math.min(
930                         visibleRect.width - (SEARCH_FIELD_SPACE * 2),
931                         SEARCH_FIELD_PREFERRED_SIZE - SEARCH_FIELD_SPACE
932                     );
933
934                 searchpanel.setBounds(
935                     Math.max(SEARCH_FIELD_SPACE, (visibleRect.x + visibleRect.width) - width),
936                     visibleRect.y + SEARCH_FIELD_SPACE, Math.min(visibleRect.width, width) - SEARCH_FIELD_SPACE,
937                     heightOfTextField
938                 );
939                 //System.err.println("Laid out search field: " + searchpanel.getBounds());
940
}
941         }
942
943         /**
944          * Removes the search field from the tree.
945          */

946         private void removeSearchField() {
947             if ((searchpanel != null) && searchpanel.isDisplayable()) {
948                 remove(searchpanel);
949                 ListView.this.getViewport().setScrollMode(originalScrollMode);
950                 this.repaint(searchpanel.getBounds());
951                 requestFocus();
952             }
953         }
954
955         private class AccessibleExplorerList extends AccessibleJList {
956             AccessibleExplorerList() {
957             }
958
959             public String JavaDoc getAccessibleName() {
960                 return ListView.this.getAccessibleContext().getAccessibleName();
961             }
962
963             public String JavaDoc getAccessibleDescription() {
964                 return ListView.this.getAccessibleContext().getAccessibleDescription();
965             }
966         }
967
968         private class SearchFieldListener extends KeyAdapter JavaDoc implements DocumentListener JavaDoc, FocusListener JavaDoc {
969             /** The last search results */
970             private List JavaDoc results = new ArrayList JavaDoc();
971
972             /** The last selected index from the search results. */
973             private int currentSelectionIndex;
974
975             SearchFieldListener() {
976             }
977
978             public void changedUpdate(DocumentEvent JavaDoc e) {
979                 searchForNode();
980             }
981
982             public void insertUpdate(DocumentEvent JavaDoc e) {
983                 searchForNode();
984             }
985
986             public void removeUpdate(DocumentEvent JavaDoc e) {
987                 searchForNode();
988             }
989
990             public void keyPressed(KeyEvent JavaDoc e) {
991                 int keyCode = e.getKeyCode();
992
993                 if (keyCode == KeyEvent.VK_ESCAPE) {
994                     removeSearchField();
995                     NbList.this.requestFocus();
996                 } else if (keyCode == KeyEvent.VK_UP) {
997                     currentSelectionIndex--;
998                     displaySearchResult();
999
1000                    // Stop processing the event here. Otherwise it's dispatched
1001
// to the tree too (which scrolls)
1002
e.consume();
1003                } else if (keyCode == KeyEvent.VK_DOWN) {
1004                    currentSelectionIndex++;
1005                    displaySearchResult();
1006
1007                    // Stop processing the event here. Otherwise it's dispatched
1008
// to the tree too (which scrolls)
1009
e.consume();
1010                } else if (keyCode == KeyEvent.VK_TAB) {
1011                    if (maxPrefix != null) {
1012                        searchTextField.setText(maxPrefix);
1013                    }
1014
1015                    e.consume();
1016                } else if (keyCode == KeyEvent.VK_ENTER) {
1017                    removeSearchField();
1018                    NbList.this.requestFocus();
1019                    NbList.this.dispatchEvent(e);
1020                }
1021            }
1022
1023            /** Searches for a node in the tree. */
1024            private void searchForNode() {
1025                currentSelectionIndex = 0;
1026                results.clear();
1027                maxPrefix = null;
1028
1029                String JavaDoc text = searchTextField.getText();
1030
1031                if (text.length() > 0) {
1032                    results = doSearch(text);
1033                    displaySearchResult();
1034                }
1035            }
1036
1037            private void displaySearchResult() {
1038                int sz = results.size();
1039
1040                if (sz > 0) {
1041                    if (currentSelectionIndex < 0) {
1042                        currentSelectionIndex = sz - 1;
1043                    } else if (currentSelectionIndex >= sz) {
1044                        currentSelectionIndex = 0;
1045                    }
1046
1047                    Integer JavaDoc index = (Integer JavaDoc) results.get(currentSelectionIndex);
1048                    list.setSelectedIndex(index.intValue());
1049                    list.ensureIndexIsVisible(index.intValue());
1050                } else {
1051                    list.clearSelection();
1052                }
1053            }
1054
1055            public void focusGained(FocusEvent JavaDoc e) {
1056                // Do nothing
1057
}
1058
1059            public void focusLost(FocusEvent JavaDoc e) {
1060                removeSearchField();
1061            }
1062        }
1063
1064        // end of navigator
1065
}
1066
1067    private final class PopupSupport extends MouseUtils.PopupMouseAdapter implements Action JavaDoc, Runnable JavaDoc {
1068        
1069        CallbackSystemAction csa;
1070        
1071        public PopupSupport() {}
1072
1073        public void mouseClicked(MouseEvent JavaDoc e) {
1074            if (MouseUtils.isDoubleClick(e)) {
1075                int index = list.locationToIndex(e.getPoint());
1076                performObjectAt(index, e.getModifiers());
1077            }
1078        }
1079
1080        protected void showPopup(MouseEvent JavaDoc e) {
1081            Point JavaDoc p = new Point JavaDoc(e.getX(), e.getY());
1082            int i = list.locationToIndex(p);
1083
1084            if (!list.isSelectedIndex(i)) {
1085                list.setSelectedIndex(i);
1086            }
1087
1088            // the area of selected
1089
Rectangle JavaDoc r = list.getCellBounds(i, i);
1090            boolean contextMenu = (r == null) || !r.contains(p);
1091
1092            createPopup(e.getX(), e.getY(), contextMenu);
1093        }
1094
1095        public void actionPerformed(ActionEvent JavaDoc e) {
1096            // XXX why later?
1097
SwingUtilities.invokeLater(this);
1098        }
1099
1100        public void run() {
1101            boolean multisel = (list.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION);
1102            int i = (multisel ? list.getLeadSelectionIndex() : list.getSelectedIndex());
1103
1104            if (i < 0) {
1105                return;
1106            }
1107
1108            Point JavaDoc p = list.indexToLocation(i);
1109
1110            if (p == null) {
1111                return;
1112            }
1113
1114            createPopup(p.x, p.y, false);
1115        }
1116
1117        public Object JavaDoc getValue(String JavaDoc key) {
1118            return null;
1119        }
1120
1121        public void putValue(String JavaDoc key, Object JavaDoc value) {}
1122
1123        public void setEnabled(boolean b) {}
1124
1125        public boolean isEnabled() {
1126            // XXX should maybe use logic in {@link #run}?
1127
return true;
1128        }
1129
1130        public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {}
1131
1132        public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {}
1133
1134    }
1135
1136    private final class Listener implements ListDataListener JavaDoc, ListSelectionListener JavaDoc, PropertyChangeListener JavaDoc,
1137        VetoableChangeListener JavaDoc {
1138        Listener() {
1139        }
1140
1141        /** Implements <code>ListDataListener</code> interface. */
1142        public void intervalAdded(ListDataEvent JavaDoc evt) {
1143            updateSelection();
1144        }
1145
1146        /** Implements <code>ListDataListener</code>. */
1147        public void intervalRemoved(ListDataEvent JavaDoc evt) {
1148            updateSelection();
1149        }
1150
1151        /** Implemetns <code>ListDataListener</code>. */
1152        public void contentsChanged(ListDataEvent JavaDoc evt) {
1153            updateSelection();
1154        }
1155
1156        public void vetoableChange(PropertyChangeEvent JavaDoc evt)
1157        throws PropertyVetoException JavaDoc {
1158            if (manager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
1159                Node[] newNodes = (Node[]) evt.getNewValue();
1160
1161                if (!selectionAccept(newNodes)) {
1162                    throw new PropertyVetoException JavaDoc("", evt); // NOI18N
1163
}
1164            }
1165        }
1166
1167        public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1168            if (manager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
1169                updateSelection();
1170
1171                return;
1172            }
1173
1174            if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) {
1175                model.setNode(manager.getExploredContext());
1176
1177                //System.out.println("Children: " + java.util.Arrays.asList (list.getValues ())); // NOI18N
1178
return;
1179            }
1180        }
1181
1182        public void valueChanged(ListSelectionEvent JavaDoc e) {
1183            int curSize = model.getSize();
1184            int[] indices = list.getSelectedIndices();
1185
1186            // bugfix #24193, check if the nodes in selection are in the view's root context
1187
List JavaDoc<Node> ll = new ArrayList JavaDoc<Node>(indices.length);
1188
1189            for (int i = 0; i < indices.length; i++) {
1190                if (indices[i] < curSize) {
1191                    Node n = Visualizer.findNode(model.getElementAt(indices[i]));
1192
1193                    if ((n == manager.getRootContext()) || (n.getParentNode() != null)) {
1194                        ll.add(n);
1195                    }
1196                } else {
1197                    // something went wrong?
1198
updateSelection();
1199
1200                    return;
1201                }
1202            }
1203
1204            Node[] nodes = ll.toArray(new Node[ll.size()]);
1205
1206            // forwarding TO E.M., so we won't listen to its cries for a while
1207
manager.removePropertyChangeListener(wlpc);
1208            manager.removeVetoableChangeListener(wlvc);
1209
1210            try {
1211                selectionChanged(nodes, manager);
1212            } catch (PropertyVetoException JavaDoc ex) {
1213                // selection vetoed - restore previous selection
1214
updateSelection();
1215            } finally {
1216                manager.addPropertyChangeListener(wlpc);
1217                manager.addVetoableChangeListener(wlvc);
1218            }
1219        }
1220    }
1221
1222    // Backspace jumps to parent folder of explored context
1223
private final class GoUpAction extends AbstractAction JavaDoc {
1224        static final long serialVersionUID = 1599999335583246715L;
1225
1226        public GoUpAction() {
1227            super("GoUpAction"); // NOI18N
1228
}
1229
1230        public void actionPerformed(ActionEvent JavaDoc e) {
1231            if (traversalAllowed) {
1232                Node pan = manager.getExploredContext();
1233                pan = pan.getParentNode();
1234
1235                if (pan != null) {
1236                    manager.setExploredContext(pan, manager.getSelectedNodes());
1237                }
1238            }
1239        }
1240
1241        public boolean isEnabled() {
1242            return true;
1243        }
1244    }
1245
1246    //Enter key performObjectAt selected index.
1247
private final class EnterAction extends AbstractAction JavaDoc {
1248        static final long serialVersionUID = -239805141416294016L;
1249
1250        public EnterAction() {
1251            super("Enter"); // NOI18N
1252
}
1253
1254        public void actionPerformed(ActionEvent JavaDoc e) {
1255            int index = list.getSelectedIndex();
1256            performObjectAt(index, e.getModifiers());
1257        }
1258
1259        public boolean isEnabled() {
1260            return true;
1261        }
1262    }
1263}
1264
Popular Tags