1 19 20 package org.netbeans.modules.search; 21 22 import java.awt.EventQueue ; 23 import java.awt.Toolkit ; 24 import static java.awt.event.InputEvent.BUTTON1_DOWN_MASK ; 25 import static java.awt.event.InputEvent.BUTTON1_MASK ; 26 import java.awt.Rectangle ; 27 import java.awt.event.ActionEvent ; 28 import java.awt.event.KeyEvent ; 29 import java.awt.event.KeyListener ; 30 import java.awt.event.MouseEvent ; 31 import java.awt.event.MouseListener ; 32 import java.util.ArrayList ; 33 import java.util.List ; 34 import javax.swing.Action ; 35 import javax.swing.JMenuItem ; 36 import javax.swing.JPopupMenu ; 37 import javax.swing.JTree ; 38 import javax.swing.event.TreeExpansionEvent ; 39 import javax.swing.event.TreeExpansionListener ; 40 import javax.swing.event.TreeWillExpandListener ; 41 import javax.swing.tree.ExpandVetoException ; 42 import javax.swing.tree.TreePath ; 43 import javax.swing.tree.TreeSelectionModel ; 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 56 final class NodeListener implements MouseListener , KeyListener , 57 TreeWillExpandListener , 58 TreeExpansionListener { 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 e) { 71 final JTree tree = (JTree ) e.getSource(); 76 77 final int clickCount = e.getClickCount(); 78 if ((clickCount != 1) && (clickCount != 2)) { 79 return; 80 } 81 82 final TreePath 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) { toggleExpansionState(tree, path); 106 } else { 107 Node nbNode = getNbNode(path, resultModel); 108 callDefaultAction(nbNode, 109 e.getSource(), 110 e.getID(), 111 "double-click"); } 113 } 114 } 115 } 116 } 117 118 private void popupTriggerEventFired(MouseEvent e) { 119 final JTree tree = (JTree ) e.getSource(); 120 final TreePath 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 134 private boolean isInsideCheckBox(final JTree tree, 135 final TreePath path, 136 final ResultModel resultModel, 137 final MouseEvent mouseEvent) { 138 if ((resultModel == null) || !resultModel.searchAndReplace) { 139 return false; 140 } 141 142 Rectangle rowRect = tree.getPathBounds(path); 143 Rectangle chRect = NodeRenderer.getCheckBoxRectangle(); 144 chRect.setLocation(rowRect.x + chRect.x, rowRect.y + chRect.y); 145 return chRect.contains(mouseEvent.getPoint()); 146 } 147 148 150 private void showPopup(final JTree tree, 151 final TreePath path, 152 final ResultModel resultModel, 153 final MouseEvent e) { 154 final int pathCount = path.getPathCount(); 155 if (pathCount == 1) { } else if (pathCount == 2) { 158 Node nbNode = getNbNode(path, resultModel); 159 if (nbNode != null) { 160 JPopupMenu popup = createFileNodePopupMenu(nbNode); 161 if (popup != null) { 162 popup.show(tree, e.getX(), e.getY()); 163 } 164 } 165 } else if (pathCount == 3) { 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 192 private boolean autocollapseFileNodeIfNeeded(JTree tree, 193 TreePath 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 215 private void toggleRootNodeSelection(final JTree tree, 216 final TreePath 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 <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 <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 281 resultTreeModel.setSelected(willBeSelected); 282 resultTreeModel.fireRootNodeChanged(); 283 284 285 if (toggledCount != 0) { 286 assert toggledIndices.length == toggledCount; 287 assert toggledObjects.length == toggledCount; 288 resultTreeModel.fireFileNodesSelectionChanged(toggledIndices, 289 toggledObjects); 290 } 291 292 293 if (expandedToggled != null) { 294 for (MatchingObject obj : expandedToggled) { 295 resultTreeModel.fireFileNodeChildrenSelectionChanged(obj); 296 } 297 } 298 } 299 300 302 private void setFileNodeSelected(JTree tree, 303 TreePath 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 324 private void toggleDetailNodeSelection(JTree 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 335 private ResultTreeModel getResultTreeModel(TreePath path) { 336 return (ResultTreeModel) path.getPathComponent(0); 337 } 338 339 341 private ResultTreeModel getResultTreeModel(JTree tree) { 342 return (ResultTreeModel) tree.getPathForRow(0).getPathComponent(0); 343 } 344 345 347 private ResultModel getResultModel(JTree tree) { 348 return ((ResultTreeModel) tree.getPathForRow(0).getPathComponent(0)) 349 .resultModel; 350 } 351 352 362 private static Node getNbNode(TreePath path, ResultModel resultModel) { 363 Node node; 364 365 Object 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 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; node = (Node) obj; 391 } 392 return node; 393 } 394 395 402 private JPopupMenu createFileNodePopupMenu(Node fileNode) { 403 Action action = getDefaultAction(fileNode); 404 if (action == null) { 405 return null; 406 } 407 408 assert action.isEnabled(); 409 410 JMenuItem menuItem = (action instanceof Presenter.Popup) 411 ? ((Presenter.Popup) action).getPopupPresenter() 412 : null; 413 JPopupMenu popupMenu = new JPopupMenu (); 414 if (menuItem != null) { 415 popupMenu.add(menuItem); 416 } else { 417 popupMenu.add(action); 418 } 419 return popupMenu; 420 } 421 422 424 private Action getDefaultAction(Node node) { 425 EditAction editAction = SharedClassObject.findObject(EditAction.class, true); 426 Action 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 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 454 private void callDefaultAction(Node node, 455 Object eventSource, 456 int eventId, 457 String command) { 458 if (node == null) { 459 Toolkit.getDefaultToolkit().beep(); 460 return; 461 } 462 463 468 EditCookie editCookie = node.getCookie(EditCookie.class); 469 if (editCookie != null) { 470 editCookie.edit(); 471 return; 472 } 473 474 475 Action 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 (eventSource, 485 eventId, 486 command)); 487 } 488 } 489 490 492 private void toggleExpansionState(JTree tree, TreePath path) { 493 if (tree.isCollapsed(path)) { 494 tree.expandPath(path); 495 } else { 496 tree.collapsePath(path); 497 } 498 } 499 500 502 private void toggleSelection(final JTree tree, 503 final TreePath 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 538 void setSelectionChangeEnabled(boolean enabled) { 539 assert EventQueue.isDispatchThread(); 540 541 this.selectionChangeEnabled = enabled; 542 } 543 544 public void keyTyped(KeyEvent e) { 545 } 546 547 public void keyReleased(KeyEvent e) { 548 if ((e.getKeyCode() != KeyEvent.VK_ENTER) 549 || (e.getModifiersEx() != 0)) { 550 return; 551 } 552 553 final JTree tree = (JTree ) e.getSource(); 554 final TreeSelectionModel selectionModel = tree.getSelectionModel(); 555 if (selectionModel.getSelectionCount() != 1) { 556 return; 557 } 558 final TreePath selectedPath = selectionModel.getLeadSelectionPath(); 559 if ((selectedPath == null) || (selectedPath.getParentPath() == null)) { 560 return; 562 } 563 Node nbNode = getNbNode(selectedPath, getResultModel(tree)); 564 callDefaultAction(nbNode, e.getSource(), e.getID(), "enter"); } 566 567 public void mouseEntered(MouseEvent e) { 568 } 569 570 public void mouseExited(MouseEvent e) { 571 } 572 573 public void mousePressed(MouseEvent e) { 574 if (e.isPopupTrigger()) { 575 popupTriggerEventFired(e); 576 } 577 } 578 579 public void mouseReleased(MouseEvent e) { 580 if (e.isPopupTrigger()) { 581 popupTriggerEventFired(e); 582 } 583 } 584 585 public void keyPressed(KeyEvent e) { 586 if (e.getKeyChar() == ' ') { 587 JTree tree = (JTree ) e.getSource(); 588 TreePath path = tree.getSelectionPath(); 589 if (path != null) { 590 toggleSelection(tree, path); 591 } 592 } 593 } 594 595 public void treeWillExpand(TreeExpansionEvent event) 596 throws ExpandVetoException { 597 final TreePath path = event.getPath(); 598 if (path.getPathCount() == 2) { 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 event) 610 throws ExpandVetoException { 611 } 612 613 public void treeExpanded(TreeExpansionEvent event) { 614 final TreePath path = event.getPath(); 615 if (path.getPathCount() == 2) { 616 ((MatchingObject) path.getLastPathComponent()).markExpanded(true); 617 } 618 } 619 620 public void treeCollapsed(TreeExpansionEvent event) { 621 final TreePath path = event.getPath(); 622 if (path.getPathCount() == 2) { 623 ((MatchingObject) path.getLastPathComponent()).markExpanded(false); 624 } 625 } 626 627 } 628 | Popular Tags |