KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > search > NodeListener


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.search;
21
22 import java.awt.EventQueue JavaDoc;
23 import java.awt.Toolkit JavaDoc;
24 import static java.awt.event.InputEvent.BUTTON1_DOWN_MASK JavaDoc;
25 import static java.awt.event.InputEvent.BUTTON1_MASK JavaDoc;
26 import java.awt.Rectangle JavaDoc;
27 import java.awt.event.ActionEvent JavaDoc;
28 import java.awt.event.KeyEvent JavaDoc;
29 import java.awt.event.KeyListener JavaDoc;
30 import java.awt.event.MouseEvent JavaDoc;
31 import java.awt.event.MouseListener JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.List JavaDoc;
34 import javax.swing.Action JavaDoc;
35 import javax.swing.JMenuItem JavaDoc;
36 import javax.swing.JPopupMenu JavaDoc;
37 import javax.swing.JTree JavaDoc;
38 import javax.swing.event.TreeExpansionEvent JavaDoc;
39 import javax.swing.event.TreeExpansionListener JavaDoc;
40 import javax.swing.event.TreeWillExpandListener JavaDoc;
41 import javax.swing.tree.ExpandVetoException JavaDoc;
42 import javax.swing.tree.TreePath JavaDoc;
43 import javax.swing.tree.TreeSelectionModel JavaDoc;
44 import org.openide.actions.EditAction;
45 import org.openide.cookies.EditCookie;
46 import org.openide.nodes.Node;
47 import org.openide.util.SharedClassObject;
48 import org.openide.util.actions.NodeAction;
49 import org.openide.util.actions.Presenter;
50 import org.openide.util.lookup.Lookups;
51
52 /**
53  *
54  * @author Marian Petras
55  */

56 final class NodeListener implements MouseListener JavaDoc, KeyListener JavaDoc,
57                                     TreeWillExpandListener JavaDoc,
58                                     TreeExpansionListener JavaDoc {
59     
60     /** */
61     private static final boolean COLLAPSE_FILE_ON_SELECTION = false;
62     /** */
63     private static final boolean COLLAPSE_FILE_ON_UNSELECTION = true;
64     /** */
65     private boolean selectionChangeEnabled = true;
66     
67     NodeListener() {
68     }
69     
70     public void mouseClicked(MouseEvent JavaDoc e) {
71         // todo (#pf): we need to solve problem between click and double
72
// click - click should be possible only on the check box area
73
// and double click should be bordered by title text.
74
// we need a test how to detect where the mouse pointer is
75
final JTree JavaDoc tree = (JTree JavaDoc) e.getSource();
76
77         final int clickCount = e.getClickCount();
78         if ((clickCount != 1) && (clickCount != 2)) {
79             return;
80         }
81         
82         final TreePath JavaDoc path = tree.getPathForLocation(e.getX(), e.getY());
83         if (path == null) {
84             return;
85         }
86
87         final ResultModel resultModel = getResultModel(tree);
88         final boolean insideCheckBox = isInsideCheckBox(tree,
89                                                         path,
90                                                         resultModel,
91                                                         e);
92         
93         final int modifiers = e.getModifiersEx();
94         if ((modifiers == 0) || (modifiers == BUTTON1_MASK)
95                              || (modifiers == BUTTON1_DOWN_MASK)) {
96             if (clickCount == 1) {
97                 if (insideCheckBox) {
98                     assert path != null;
99                     toggleSelection(tree, path);
100                 }
101             }
102             if (clickCount == 2) {
103                 if (!insideCheckBox) {
104                     if (path.getPathCount() == 1) { //root node
105
toggleExpansionState(tree, path);
106                     } else {
107                         Node nbNode = getNbNode(path, resultModel);
108                         callDefaultAction(nbNode,
109                                           e.getSource(),
110                                           e.getID(),
111                                           "double-click"); //NOI18N
112
}
113                 }
114             }
115         }
116     }
117     
118     private void popupTriggerEventFired(MouseEvent JavaDoc e) {
119         final JTree JavaDoc tree = (JTree JavaDoc) e.getSource();
120         final TreePath JavaDoc path = tree.getPathForLocation(e.getX(), e.getY());
121         if (path == null) {
122             return;
123         }
124
125         final ResultModel resultModel = getResultModel(tree);
126         tree.setSelectionPath(path);
127         if (!isInsideCheckBox(tree, path, resultModel, e)) {
128             showPopup(tree, path, resultModel, e);
129         }
130     }
131     
132     /**
133      */

134     private boolean isInsideCheckBox(final JTree JavaDoc tree,
135                                      final TreePath JavaDoc path,
136                                      final ResultModel resultModel,
137                                      final MouseEvent JavaDoc mouseEvent) {
138         if ((resultModel == null) || !resultModel.searchAndReplace) {
139             return false;
140         }
141         
142         Rectangle JavaDoc rowRect = tree.getPathBounds(path);
143         Rectangle JavaDoc chRect = NodeRenderer.getCheckBoxRectangle();
144         chRect.setLocation(rowRect.x + chRect.x, rowRect.y + chRect.y);
145         return chRect.contains(mouseEvent.getPoint());
146     }
147     
148     /**
149      */

150     private void showPopup(final JTree JavaDoc tree,
151                            final TreePath JavaDoc path,
152                            final ResultModel resultModel,
153                            final MouseEvent JavaDoc e) {
154         final int pathCount = path.getPathCount();
155         if (pathCount == 1) { //root node
156
//no popup-menu for the root node
157
} else if (pathCount == 2) {
158             Node nbNode = getNbNode(path, resultModel);
159             if (nbNode != null) {
160                 JPopupMenu JavaDoc popup = createFileNodePopupMenu(nbNode);
161                 if (popup != null) {
162                     popup.show(tree, e.getX(), e.getY());
163                 }
164             }
165         } else if (pathCount == 3) { //detail node
166
Node nbNode = getNbNode(path, resultModel);
167             if (nbNode != null) {
168                 nbNode.getContextMenu().show(tree, e.getX(), e.getY());
169             }
170         } else {
171             assert false;
172         }
173     }
174     
175     /**
176      * Auto-collapses a given file node if appropriate after
177      * the select or unselect operation. The behaviour is given by constants
178      * {@link #COLLAPSE_FILE_ON_SELECTION}
179      * and {@link #COLLAPSE_FILE_ON_UNSELECTION}.
180      *
181      * @param tree tree in which the node resides
182      * @param treePath path to the file node
183      * @param matchingObj object containing the file
184      * @param selected determines the event that caused this method
185      * to be called
186      * - {@code true} for selection,
187      * {@code false} for unselection
188      * @return final expansion state of the node
189      * - {@code true} if the node is now collapsed,
190      * {@code false} if the node is now expanded
191      */

192     private boolean autocollapseFileNodeIfNeeded(JTree JavaDoc tree,
193                                                  TreePath JavaDoc treePath,
194                                                  MatchingObject matchingObj,
195                                                  boolean selected) {
196         assert treePath.getPathCount() == 2
197                && treePath.getLastPathComponent() == matchingObj;
198         
199         final boolean autocollapse = selected ? COLLAPSE_FILE_ON_SELECTION
200                                               : COLLAPSE_FILE_ON_UNSELECTION;
201         boolean isCollapsed;
202         if (autocollapse) {
203             if (matchingObj.isExpanded()) {
204                 tree.collapsePath(treePath);
205             }
206             isCollapsed = true;
207         } else {
208             isCollapsed = !tree.isExpanded(treePath);
209         }
210         return isCollapsed;
211     }
212     
213     /**
214      */

215     private void toggleRootNodeSelection(final JTree JavaDoc tree,
216                                          final TreePath JavaDoc path) {
217         final ResultTreeModel resultTreeModel = getResultTreeModel(path);
218         final ResultModel resultModel = resultTreeModel.resultModel;
219         final boolean isSelected = resultTreeModel.isSelected();
220         final boolean willBeSelected = !isSelected;
221         final boolean autocollapse = willBeSelected
222                                      ? COLLAPSE_FILE_ON_SELECTION
223                                      : COLLAPSE_FILE_ON_UNSELECTION;
224
225         final MatchingObject[] matchingObjects
226                                     = resultModel.getMatchingObjects();
227
228         int[] toggledIndices = null;
229         MatchingObject[] toggledObjects = null;
230         List JavaDoc<MatchingObject> expandedToggled = null;
231         int toggledCount = 0;
232         for (int i = 0; i < matchingObjects.length; i++) {
233             final MatchingObject matchingObj = matchingObjects[i];
234
235             boolean collapsed = !matchingObj.isExpanded();
236             if (autocollapse && !collapsed) {
237                 tree.collapsePath(path.pathByAddingChild(matchingObj));
238                 collapsed = true;
239             }
240
241             if (matchingObj.isSelected() == willBeSelected) {
242                 continue;
243             }
244
245             matchingObj.setSelected(willBeSelected);
246             if (toggledCount == 0) {
247                 int arrayLength = matchingObjects.length - i;
248                 toggledIndices = new int[arrayLength];
249                 toggledObjects = new MatchingObject[arrayLength];
250             }
251             toggledIndices[toggledCount] = i;
252             toggledObjects[toggledCount] = matchingObj;
253             toggledCount++;
254
255             if (collapsed) {
256                 matchingObj.markChildrenSelectionDirty();
257             } else {
258                 if (expandedToggled == null) {
259                     expandedToggled = new ArrayList JavaDoc<MatchingObject>(6);
260                 }
261                 expandedToggled.add(matchingObj);
262             }
263         }
264         if (toggledCount != 0 && toggledCount != matchingObjects.length) {
265
266             int[] newToggledIndices = new int[toggledCount];
267             System.arraycopy(toggledIndices, 0,
268                              newToggledIndices, 0,
269                              toggledCount);
270             toggledIndices = newToggledIndices;
271
272             MatchingObject[] newToggledObjects
273                                     = new MatchingObject[toggledCount];
274             System.arraycopy(toggledObjects, 0,
275                              newToggledObjects, 0,
276                              toggledCount);
277             toggledObjects = newToggledObjects;
278         }
279
280         /* Update selection of the root node: */
281         resultTreeModel.setSelected(willBeSelected);
282         resultTreeModel.fireRootNodeChanged();
283
284         /* Update selection of file nodes: */
285         if (toggledCount != 0) {
286             assert toggledIndices.length == toggledCount;
287             assert toggledObjects.length == toggledCount;
288             resultTreeModel.fireFileNodesSelectionChanged(toggledIndices,
289                                                           toggledObjects);
290         }
291
292         /* Update selection of visible detail nodes: */
293         if (expandedToggled != null) {
294             for (MatchingObject obj : expandedToggled) {
295                 resultTreeModel.fireFileNodeChildrenSelectionChanged(obj);
296             }
297         }
298     }
299     
300     /**
301      */

302     private void setFileNodeSelected(JTree JavaDoc tree,
303                                      TreePath JavaDoc treePath,
304                                      MatchingObject matchingObj,
305                                      boolean selected) {
306         assert treePath.getPathCount() == 2;
307         
308         boolean collapsed = autocollapseFileNodeIfNeeded(tree,
309                                                          treePath,
310                                                          matchingObj,
311                                                          selected);
312         boolean deferChildrenSelection = collapsed;
313         matchingObj.setSelected(selected);
314         getResultTreeModel(treePath).fireFileNodeSelectionChanged(
315                                                 matchingObj,
316                                                 !deferChildrenSelection);
317         if (deferChildrenSelection) {
318             matchingObj.markChildrenSelectionDirty();
319         }
320     }
321     
322     /**
323      */

324     private void toggleDetailNodeSelection(JTree JavaDoc tree,
325                                            ResultModel resultModel,
326                                            MatchingObject matchingObj,
327                                            int index) {
328         matchingObj.toggleSubnodeSelection(resultModel, index);
329         getResultTreeModel(tree).fireDetailNodeSelectionChanged(matchingObj,
330                                                                 index);
331     }
332     
333     /**
334      */

335     private ResultTreeModel getResultTreeModel(TreePath JavaDoc path) {
336         return (ResultTreeModel) path.getPathComponent(0);
337     }
338     
339     /**
340      */

341     private ResultTreeModel getResultTreeModel(JTree JavaDoc tree) {
342         return (ResultTreeModel) tree.getPathForRow(0).getPathComponent(0);
343     }
344     
345     /**
346      */

347     private ResultModel getResultModel(JTree JavaDoc tree) {
348         return ((ResultTreeModel) tree.getPathForRow(0).getPathComponent(0))
349                .resultModel;
350     }
351     
352     /**
353      * Returns a NetBeans explorer node corresponding to the given object.
354      *
355      * @param path identifies the object for which a NetBeans explorer node
356      * should be returned
357      * @param resultModel model of search results
358      * @return node corresponding to the given object,
359      * or {@code null} if the {@link MatchingObject} representing
360      * the given object is not {@link MatchingObject#isValid}
361      */

362     private static Node getNbNode(TreePath JavaDoc path, ResultModel resultModel) {
363         Node node;
364         
365         Object JavaDoc obj = path.getLastPathComponent();
366         
367         MatchingObject matchingObj;
368         boolean isFileNode;
369         
370         if (obj.getClass() == MatchingObject.class) {
371             matchingObj = (MatchingObject) obj;
372             
373             isFileNode = true;
374         } else {
375             Object JavaDoc parentObj = path.getParentPath().getLastPathComponent();
376             assert parentObj.getClass() == MatchingObject.class;
377             matchingObj = (MatchingObject) parentObj;
378             
379             isFileNode = false;
380         }
381         if (!matchingObj.isObjectValid()) {
382             return null;
383         }
384         
385         if (isFileNode) {
386             node = resultModel.getSearchGroup().getNodeForFoundObject(
387                                                             matchingObj.object);
388         } else {
389             assert obj instanceof Node; //detail node
390
node = (Node) obj;
391         }
392         return node;
393     }
394     
395     /**
396      * Creates a popup-menu for the given file node.
397      *
398      * @param fileNode file node for which a popup-menu should be built
399      * @return created popup-menu; or {@code null} if no popup-menu
400      * is available for the given file node
401      */

402     private JPopupMenu JavaDoc createFileNodePopupMenu(Node fileNode) {
403         Action JavaDoc action = getDefaultAction(fileNode);
404         if (action == null) {
405             return null;
406         }
407         
408         assert action.isEnabled();
409         
410         JMenuItem JavaDoc menuItem = (action instanceof Presenter.Popup)
411                              ? ((Presenter.Popup) action).getPopupPresenter()
412                              : null;
413         JPopupMenu JavaDoc popupMenu = new JPopupMenu JavaDoc();
414         if (menuItem != null) {
415             popupMenu.add(menuItem);
416         } else {
417             popupMenu.add(action);
418         }
419         return popupMenu;
420     }
421     
422     /**
423      */

424     private Action JavaDoc getDefaultAction(Node node) {
425         EditAction editAction = SharedClassObject.findObject(EditAction.class, true);
426         Action JavaDoc action;
427         if (editAction != null) {
428             action = editAction.createContextAwareInstance(
429                                                     Lookups.singleton(node));
430             if (action.isEnabled()) {
431                 return action;
432             }
433         }
434         
435         Action JavaDoc preferredAction = node.getPreferredAction();
436         if (preferredAction == null) {
437             return null;
438         }
439         
440         action = preferredAction;
441         if (action instanceof NodeAction) {
442             action = ((NodeAction) action).createContextAwareInstance(
443                                                        Lookups.singleton(node));
444         }
445         return ((action != null) && action.isEnabled()) ? action : null;
446     }
447     
448     /**
449      * @param node node on which the default action should be called,
450      * or {@code null} if the user action was called on an invalid
451      * object
452      * @see MatchingObject#isValid
453      */

454     private void callDefaultAction(Node node,
455                                    Object JavaDoc eventSource,
456                                    int eventId,
457                                    String JavaDoc command) {
458         if (node == null) {
459             Toolkit.getDefaultToolkit().beep();
460             return;
461         }
462         
463         /*
464          * Before trying the actual default action, try EditCookie first.
465          * This changes the behaviour such that forms are opened in the edit
466          * mode instead of the visual form editor.
467          */

468         EditCookie editCookie = node.getCookie(EditCookie.class);
469         if (editCookie != null) {
470             editCookie.edit();
471             return;
472         }
473         
474         /* No EditCookie? So try the default action now. */
475         Action JavaDoc action = node.getPreferredAction();
476         if (action == null) {
477             return;
478         }
479         if (action instanceof NodeAction) {
480             action = ((NodeAction) action).createContextAwareInstance(
481                                                        Lookups.singleton(node));
482         }
483         if ((action != null) && action.isEnabled()) {
484             action.actionPerformed(new ActionEvent JavaDoc(eventSource,
485                                                    eventId,
486                                                    command));
487         }
488     }
489     
490     /**
491      */

492     private void toggleExpansionState(JTree JavaDoc tree, TreePath JavaDoc path) {
493         if (tree.isCollapsed(path)) {
494             tree.expandPath(path);
495         } else {
496             tree.collapsePath(path);
497         }
498     }
499     
500     /**
501      */

502     private void toggleSelection(final JTree JavaDoc tree,
503                                  final TreePath JavaDoc path) {
504         assert EventQueue.isDispatchThread();
505         
506         if (!selectionChangeEnabled) {
507             return;
508         }
509         
510         final int pathCount = path.getPathCount();
511         if (pathCount == 1) {
512             toggleRootNodeSelection(tree, path);
513
514         } else if (pathCount == 2) {
515             MatchingObject matchingObj
516                     = (MatchingObject) path.getPathComponent(1);
517             setFileNodeSelected(tree,
518                                 path,
519                                 matchingObj,
520                                 !matchingObj.isSelected());
521
522         } else {
523             assert pathCount == 3;
524             MatchingObject matchingObj
525                     = (MatchingObject) path.getPathComponent(1);
526             int parentPathRow = tree.getRowForPath(path.getParentPath());
527             int row = tree.getRowForPath(path);
528             int index = row - parentPathRow - 1;
529             toggleDetailNodeSelection(tree,
530                                       getResultModel(tree),
531                                       matchingObj,
532                                       index);
533         }
534     }
535     
536     /**
537      */

538     void setSelectionChangeEnabled(boolean enabled) {
539         assert EventQueue.isDispatchThread();
540         
541         this.selectionChangeEnabled = enabled;
542     }
543
544     public void keyTyped(KeyEvent JavaDoc e) {
545     }
546     
547     public void keyReleased(KeyEvent JavaDoc e) {
548         if ((e.getKeyCode() != KeyEvent.VK_ENTER)
549                 || (e.getModifiersEx() != 0)) {
550             return;
551         }
552         
553         final JTree JavaDoc tree = (JTree JavaDoc) e.getSource();
554         final TreeSelectionModel JavaDoc selectionModel = tree.getSelectionModel();
555         if (selectionModel.getSelectionCount() != 1) {
556             return;
557         }
558         final TreePath JavaDoc selectedPath = selectionModel.getLeadSelectionPath();
559         if ((selectedPath == null) || (selectedPath.getParentPath() == null)) {
560             // empty selection or root node selected
561
return;
562         }
563         Node nbNode = getNbNode(selectedPath, getResultModel(tree));
564         callDefaultAction(nbNode, e.getSource(), e.getID(), "enter"); //NOI18N
565
}
566     
567     public void mouseEntered(MouseEvent JavaDoc e) {
568     }
569     
570     public void mouseExited(MouseEvent JavaDoc e) {
571     }
572     
573     public void mousePressed(MouseEvent JavaDoc e) {
574         if (e.isPopupTrigger()) {
575             popupTriggerEventFired(e);
576         }
577     }
578     
579     public void mouseReleased(MouseEvent JavaDoc e) {
580         if (e.isPopupTrigger()) {
581             popupTriggerEventFired(e);
582         }
583     }
584     
585     public void keyPressed(KeyEvent JavaDoc e) {
586         if (e.getKeyChar() == ' ') {
587             JTree JavaDoc tree = (JTree JavaDoc) e.getSource();
588             TreePath JavaDoc path = tree.getSelectionPath();
589             if (path != null) {
590                 toggleSelection(tree, path);
591             }
592         }
593     }
594
595     public void treeWillExpand(TreeExpansionEvent JavaDoc event)
596                                             throws ExpandVetoException JavaDoc {
597         final TreePath JavaDoc path = event.getPath();
598         if (path.getPathCount() == 2) { //file node
599
MatchingObject matchingObj = (MatchingObject)
600                                          path.getLastPathComponent();
601             if (matchingObj.isChildrenSelectionDirty()) {
602                 getResultTreeModel(path)
603                         .fireFileNodeChildrenSelectionChanged(matchingObj);
604                 matchingObj.markChildrenSelectionClean();
605             }
606         }
607     }
608
609     public void treeWillCollapse(TreeExpansionEvent JavaDoc event)
610                                             throws ExpandVetoException JavaDoc {
611     }
612
613     public void treeExpanded(TreeExpansionEvent JavaDoc event) {
614         final TreePath JavaDoc path = event.getPath();
615         if (path.getPathCount() == 2) {
616             ((MatchingObject) path.getLastPathComponent()).markExpanded(true);
617         }
618     }
619
620     public void treeCollapsed(TreeExpansionEvent JavaDoc event) {
621         final TreePath JavaDoc path = event.getPath();
622         if (path.getPathCount() == 2) {
623             ((MatchingObject) path.getLastPathComponent()).markExpanded(false);
624         }
625     }
626     
627 }
628
Popular Tags