1 19 package org.openide.explorer.view; 20 21 import java.util.logging.Logger ; 22 import org.openide.awt.MouseUtils; 23 import org.openide.nodes.Node; 24 import org.openide.nodes.Node.Property; 25 import org.openide.util.NbBundle; 26 import org.openide.explorer.ExplorerManager; 27 import org.openide.explorer.ExplorerManager.Provider; 28 import org.openide.explorer.ExplorerUtils; 29 30 import java.awt.*; 31 import java.awt.event.*; 32 33 import java.beans.PropertyChangeEvent ; 34 import java.beans.PropertyChangeListener ; 35 36 import java.util.ArrayList ; 37 import java.util.Comparator ; 38 import java.util.Enumeration ; 39 import java.util.HashSet ; 40 import java.util.Iterator ; 41 import java.util.logging.Level ; 42 43 import javax.accessibility.AccessibleContext ; 44 45 import javax.swing.*; 46 import javax.swing.border.Border ; 47 import javax.swing.event.*; 48 import javax.swing.plaf.metal.MetalScrollBarUI ; 49 import javax.swing.table.*; 50 import javax.swing.tree.*; 51 import org.openide.explorer.view.TreeView.PopupAdapter; 52 import org.openide.explorer.view.TreeView.PopupSupport; 53 import org.openide.explorer.view.TreeView.TreePropertyListener; 54 55 56 169 public class TreeTableView extends BeanTreeView { 170 private static final String COLUMNS_ICON = "/org/netbeans/modules/openide/explorer/columns.gif"; 173 private static final String SORT_ASC_ICON = "org/netbeans/modules/openide/explorer/columnsSortedAsc.gif"; private static final String SORT_DESC_ICON = "org/netbeans/modules/openide/explorer/columnsSortedDesc.gif"; 177 178 protected JTable treeTable; 179 private NodeTableModel tableModel; 180 181 private JScrollBar hScrollBar; 183 private JScrollPane scrollPane; 184 private ScrollListener listener; 185 186 private boolean allowHideColumns = false; 188 189 private boolean allowSortingByColumn = false; 191 192 private boolean hideHScrollBar = false; 194 195 private JButton colsButton = null; 197 198 private SortedNodeTreeModel sortedNodeTreeModel; 200 201 202 private ActionListener defaultTreeActionListener; 203 204 private TableCellRenderer defaultHeaderRenderer = null; 206 private MouseUtils.PopupMouseAdapter tableMouseListener; 207 208 209 private AccessibleContext accessContext; 210 private TreeColumnProperty treeColumnProperty = new TreeColumnProperty(); 211 private int treeColumnWidth; 212 private Component treeTableParent = null; 213 214 216 public TreeTableView() { 217 this(new NodeTableModel()); 218 } 219 220 223 public TreeTableView(NodeTableModel ntm) { 224 tableModel = ntm; 225 226 initializeTreeTable(); 227 setPopupAllowed(true); 228 setDefaultActionAllowed(true); 229 230 initializeTreeScrollSupport(); 231 232 JPanel p = new CompoundScrollPane(); 234 p.setLayout(new BorderLayout()); 235 scrollPane.setViewportView(treeTable); 236 p.add(BorderLayout.CENTER, scrollPane); 237 238 ImageIcon ic = new ImageIcon(TreeTable.class.getResource(COLUMNS_ICON)); colsButton = new javax.swing.JButton (ic); 240 colsButton.addActionListener( 241 new ActionListener() { 242 public void actionPerformed(ActionEvent evt) { 243 selectVisibleColumns(); 244 } 245 } 246 ); 247 248 JPanel sbp = new JPanel(); 249 sbp.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); 250 sbp.add(hScrollBar); 251 p.add(BorderLayout.SOUTH, sbp); 252 253 super.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 254 super.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 255 setViewportView(p); 256 setBorder(BorderFactory.createEmptyBorder()); setViewportBorder(BorderFactory.createEmptyBorder()); } 259 260 public void setRowHeader(JViewport rowHeader) { 261 rowHeader.setBorder(BorderFactory.createEmptyBorder()); 262 super.setRowHeader(rowHeader); 263 } 264 265 267 public void setHorizontalScrollBarPolicy(int policy) { 268 hideHScrollBar = (policy == JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 269 270 if (hideHScrollBar) { 271 hScrollBar.setVisible(false); 272 ((TreeTable) treeTable).setTreeHScrollingEnabled(false); 273 } 274 } 275 276 278 public void setVerticalScrollBarPolicy(int policy) { 279 if (scrollPane == null) { 280 return; 281 } 282 283 allowHideColumns = (policy == JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 284 285 if (allowHideColumns) { 286 scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, colsButton); 287 } 288 289 treeTable.getTableHeader().setReorderingAllowed(allowHideColumns); 290 291 scrollPane.setVerticalScrollBarPolicy(policy); 292 } 293 294 protected NodeTreeModel createModel() { 295 return getSortedNodeTreeModel(); 296 } 297 298 299 public void requestFocus() { 300 if (treeTable != null) { 301 treeTable.requestFocus(); 302 } 303 } 304 305 public boolean requestFocusInWindow() { 306 boolean res = super.requestFocusInWindow(); 307 308 if (null != treeTable) { 310 treeTable.requestFocus(); 311 } 312 313 return res; 314 } 315 316 318 private void setAllowSortingByColumn(boolean allow) { 319 if (allow && (allow != allowSortingByColumn)) { 320 addMouseListener( 321 new MouseAdapter() { 322 public void mouseClicked(MouseEvent evt) { 323 if (evt.getClickCount() == 0) return ; 325 Component c = evt.getComponent(); 326 327 if (c instanceof JTableHeader) { 328 JTableHeader h = (JTableHeader) c; 329 int index = h.columnAtPoint(evt.getPoint()); 330 331 if (index >= 0) { 335 clickOnColumnAction(index - 1); 336 } 337 } 338 } 339 } 340 ); 341 } 342 343 allowSortingByColumn = allow; 344 } 345 346 349 private void clickOnColumnAction(int index) { 350 if (index == -1) { 351 if (treeColumnProperty.isComparable()) { 352 if (treeColumnProperty.isSortingColumn()) { 353 if (!treeColumnProperty.isSortOrderDescending()) { 354 setSortingOrder(false); 355 } else { 356 noSorting(); 357 } 358 } else { 359 int realIndex = tableModel.translateVisibleColumnIndex(index); 360 setSortingColumn(index); 361 setSortingOrder(true); 362 } 363 } 364 } else if (tableModel.isComparableColumn(index)) { 365 if (tableModel.isSortingColumnEx(tableModel.translateVisibleColumnIndex(index))) { 366 if (!tableModel.isSortOrderDescending()) { 367 setSortingOrder(false); 368 } else { 369 noSorting(); 370 } 371 } else { 372 int realIndex = tableModel.translateVisibleColumnIndex(index); 373 setSortingColumn(realIndex); 374 setSortingOrder(true); 375 } 376 } 377 } 378 379 private void selectVisibleColumns() { 380 setCurrentWidths(); 381 382 String viewName = null; 383 384 if (getParent() != null) { 385 viewName = getParent().getName(); 386 } 387 388 if ( 389 tableModel.selectVisibleColumns( 390 viewName, treeTable.getColumnName(0), getSortedNodeTreeModel().getRootDescription() 391 ) 392 ) { 393 if (tableModel.getSortingColumn() == -1) { 394 getSortedNodeTreeModel().setSortedByProperty(null); 395 } 396 397 setTreePreferredWidth(treeColumnWidth); 398 399 for (int i = 0; i < tableModel.getColumnCount(); i++) { 400 setTableColumnPreferredWidth(tableModel.getArrayIndex(i), tableModel.getVisibleColumnWidth(i)); 401 } 402 } 403 } 404 405 private void setCurrentWidths() { 406 treeColumnWidth = treeTable.getColumnModel().getColumn(0).getWidth(); 407 408 for (int i = 0; i < tableModel.getColumnCount(); i++) { 409 int w = treeTable.getColumnModel().getColumn(i + 1).getWidth(); 410 tableModel.setVisibleColumnWidth(i, w); 411 } 412 } 413 414 419 void initializeTree() { 420 } 421 422 424 private void initializeTreeTable() { 425 treeModel = createModel(); 426 treeTable = new TreeTable(treeModel, tableModel); 427 tree = ((TreeTable) treeTable).getTree(); 428 429 defaultHeaderRenderer = treeTable.getTableHeader().getDefaultRenderer(); 430 treeTable.getTableHeader().setDefaultRenderer(new SortingHeaderRenderer()); 431 432 managerListener = new TreePropertyListener(); 434 tree.addTreeExpansionListener(managerListener); 435 436 tree.addTreeExpansionListener( 438 new TreeExpansionListener() { 439 public void treeExpanded(TreeExpansionEvent event) { 440 TreePath path = event.getPath(); 441 442 if (path != null) { 443 Enumeration en = TreeTableView.this.tree.getExpandedDescendants(path); 446 447 getSortedNodeTreeModel().sortChildren((VisualizerNode) path.getLastPathComponent(), true); 449 450 while (en.hasMoreElements()) { 452 TreeTableView.this.tree.expandPath((TreePath) en.nextElement()); 453 } 454 } 455 } 456 457 public void treeCollapsed(TreeExpansionEvent event) { 458 } 460 } 461 ); 462 463 defaultActionListener = new PopupSupport(); 464 Action popupWrapper = new AbstractAction() { 465 public void actionPerformed(ActionEvent evt) { 466 SwingUtilities.invokeLater( defaultActionListener ); 467 } 468 469 public boolean isEnabled() { 470 return treeTable.isFocusOwner() || tree.isFocusOwner(); 471 } 472 }; 473 474 treeTable.getInputMap( JTree.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put( 475 KeyStroke.getKeyStroke( KeyEvent.VK_F10, KeyEvent.SHIFT_DOWN_MASK ), "org.openide.actions.PopupAction" ); 476 treeTable.getActionMap().put("org.openide.actions.PopupAction", popupWrapper); 477 tree.addMouseListener(defaultActionListener); 478 479 tableMouseListener = new MouseUtils.PopupMouseAdapter() { 480 public void showPopup(MouseEvent mevt) { 481 if (isPopupAllowed()) { 482 if (mevt.getY() > treeTable.getHeight()) { 483 treeTable.clearSelection(); 485 } else { 486 int selRow = treeTable.rowAtPoint( mevt.getPoint() ); 487 boolean isAlreadySelected = false; 488 int[] currentSelection = tree.getSelectionRows(); 489 for( int i=0; null != currentSelection && i<currentSelection.length; i++ ) { 490 if( selRow == currentSelection[i] ) { 491 isAlreadySelected = true; 492 break; 493 } 494 } 495 if( !isAlreadySelected ) 496 tree.setSelectionRow( selRow ); 497 } 498 499 createPopup(mevt); 500 } 501 } 502 }; 503 treeTable.addMouseListener(tableMouseListener); 504 505 if (UIManager.getColor("control") != null) { treeTable.setGridColor(UIManager.getColor("control")); } 508 } 509 510 public void setSelectionMode(int mode) { 511 super.setSelectionMode(mode); 512 513 if (mode == TreeSelectionModel.SINGLE_TREE_SELECTION) { 514 treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 515 } else if (mode == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) { 516 treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 517 } else if (mode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) { 518 treeTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 519 } 520 } 521 522 524 public AccessibleContext getAccessibleContext() { 525 if (accessContext == null) { 526 accessContext = new AccessibleTreeTableView(); 527 } 528 529 return accessContext; 530 } 531 532 534 private void initializeTreeScrollSupport() { 535 scrollPane = new JScrollPane(); 536 scrollPane.setName("TreeTableView.scrollpane"); scrollPane.setBorder(BorderFactory.createEmptyBorder()); 538 scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); 539 540 if (UIManager.getColor("Table.background") != null) { scrollPane.getViewport().setBackground(UIManager.getColor("Table.background")); } 543 544 hScrollBar = new JScrollBar(JScrollBar.HORIZONTAL); 545 hScrollBar.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE); 546 hScrollBar.setVisible(false); 547 548 listener = new ScrollListener(); 549 550 treeTable.addPropertyChangeListener(listener); 551 scrollPane.getViewport().addComponentListener(listener); 552 tree.addPropertyChangeListener(listener); 553 hScrollBar.getModel().addChangeListener(listener); 554 } 555 556 558 public void setPopupAllowed(boolean value) { 559 if (tree == null) { 560 return; 561 } 562 563 if ((popupListener == null) && value) { 564 popupListener = new PopupAdapter() { 566 protected void showPopup(MouseEvent e) { 567 int selRow = tree.getClosestRowForLocation(e.getX(), e.getY()); 568 569 if (!tree.isRowSelected(selRow)) { 570 tree.setSelectionRow(selRow); 571 } 572 } 573 }; 574 575 tree.addMouseListener(popupListener); 576 577 return; 578 } 579 580 if ((popupListener != null) && !value) { 581 tree.removeMouseListener(popupListener); 583 popupListener = null; 584 585 return; 586 } 587 } 588 589 591 public void setDefaultActionAllowed(boolean value) { 592 if (tree == null) { 593 return; 594 } 595 596 defaultActionEnabled = value; 597 598 if (value) { 599 defaultTreeActionListener = new DefaultTreeAction(); 600 treeTable.registerKeyboardAction( 601 defaultTreeActionListener, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), JComponent.WHEN_FOCUSED 602 ); 603 } else { 604 defaultTreeActionListener = null; 606 treeTable.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false)); 607 } 608 } 609 610 613 public void setProperties(Property[] props) { 614 tableModel.setProperties(props); 615 treeColumnProperty.setProperty(tableModel.propertyForColumn(-1)); 616 617 if (treeColumnProperty.isComparable() || tableModel.existsComparableColumn()) { 618 setAllowSortingByColumn(true); 619 620 if (treeColumnProperty.isSortingColumn()) { 621 getSortedNodeTreeModel().setSortedByName(true, !treeColumnProperty.isSortOrderDescending()); 622 } else { 623 int index = tableModel.getSortingColumn(); 624 625 if (index != -1) { 626 getSortedNodeTreeModel().setSortedByProperty( 627 tableModel.propertyForColumnEx(index), !tableModel.isSortOrderDescending() 628 ); 629 } 630 } 631 } 632 } 633 634 642 public final void setTableAutoResizeMode(int mode) { 643 treeTable.setAutoResizeMode(mode); 644 } 645 646 654 public final int getTableAutoResizeMode() { 655 return treeTable.getAutoResizeMode(); 656 } 657 658 662 public final void setTableColumnPreferredWidth(int index, int width) { 663 if (index == -1) { 664 return; 666 } 667 668 tableModel.setArrayColumnWidth(index, width); 669 670 int j = tableModel.getVisibleIndex(index); 671 672 if (j != -1) { 673 treeTable.getColumnModel().getColumn(j + 1).setPreferredWidth(width); 674 } 675 } 676 677 681 public final int getTableColumnPreferredWidth(int index) { 682 int j = tableModel.getVisibleIndex(index); 683 684 if (j != -1) { 685 return treeTable.getColumnModel().getColumn(j + 1).getPreferredWidth(); 686 } else { 687 return tableModel.getArrayColumnWidth(index); 688 } 689 } 690 691 694 public final void setTreePreferredWidth(int width) { 695 treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()).setPreferredWidth(width); 696 } 697 698 701 public final int getTreePreferredWidth() { 702 return treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()).getPreferredWidth(); 703 } 704 705 public void addNotify() { 706 if (treeTable.getParent() != null) { 708 treeTableParent = treeTable.getParent(); 709 treeTableParent.addMouseListener(tableMouseListener); 710 } 711 712 super.addNotify(); 713 listener.revalidateScrollBar(); 714 } 715 716 public void removeNotify() { 717 super.removeNotify(); 718 719 if (treeTableParent != null) { treeTableParent.removeMouseListener(tableMouseListener); 721 } 722 723 treeTableParent = null; 724 725 tableModel.setNodes(new Node[] { }); 727 } 728 729 public void addMouseListener(MouseListener l) { 730 super.addMouseListener(l); 731 treeTable.getTableHeader().addMouseListener(l); 732 } 733 734 public void removeMouseListener(MouseListener l) { 735 super.removeMouseListener(l); 736 treeTable.getTableHeader().removeMouseListener(l); 737 } 738 739 741 public void setDragSource(boolean state) { 742 } 743 744 746 public void setDropTarget(boolean state) { 747 } 748 749 751 Point getPositionForPopup() { 752 int row = treeTable.getSelectedRow(); 753 754 if (row < 0) { 755 return null; 756 } 757 758 int col = treeTable.getSelectedColumn(); 759 760 if (col < 0) { 761 col = 0; 762 } 763 764 Rectangle r = null; 765 766 if (col == 0) { 767 r = tree.getRowBounds(row); 768 } else { 769 r = treeTable.getCellRect(row, col, true); 770 } 771 772 Point p = SwingUtilities.convertPoint(treeTable, r.x, r.y, this); 773 774 return p; 775 } 776 777 private void createPopup(MouseEvent e) { 778 Point p = SwingUtilities.convertPoint(e.getComponent(), e.getX(), e.getY(), TreeTableView.this); 779 780 createPopup(p.x, p.y); 781 782 e.consume(); 783 } 784 785 void createPopup(int xpos, int ypos) { 786 int treeXpos = xpos - ((TreeTable) treeTable).getPositionX(); 787 788 if (allowHideColumns || allowSortingByColumn) { 789 int col = treeTable.getColumnModel().getColumnIndexAtX(treeXpos); 790 super.createExtendedPopup(xpos, ypos, getListMenu(col)); 791 } else { 792 super.createPopup(xpos, ypos); 793 } 794 } 795 796 798 private JMenu getListMenu(final int col) { 799 JMenu listItem = new JMenu(NbBundle.getBundle(NodeTableModel.class).getString("LBL_ListOptions")); 800 801 if (allowHideColumns && (col > 0)) { 802 JMenu colsItem = new JMenu(NbBundle.getBundle(NodeTableModel.class).getString("LBL_ColsMenu")); 803 804 boolean addColsItem = false; 805 806 if (col > 1) { 807 JMenuItem moveLItem = new JMenuItem(NbBundle.getBundle(NodeTableModel.class).getString("LBL_MoveLeft")); 808 moveLItem.addActionListener( 809 new ActionListener() { 810 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 811 treeTable.getColumnModel().moveColumn(col, col - 1); 812 } 813 } 814 ); 815 colsItem.add(moveLItem); 816 addColsItem = true; 817 } 818 819 if (col < tableModel.getColumnCount()) { 820 JMenuItem moveRItem = new JMenuItem( 821 NbBundle.getBundle(NodeTableModel.class).getString("LBL_MoveRight") 822 ); 823 moveRItem.addActionListener( 824 new ActionListener() { 825 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 826 treeTable.getColumnModel().moveColumn(col, col + 1); 827 } 828 } 829 ); 830 colsItem.add(moveRItem); 831 addColsItem = true; 832 } 833 834 if (addColsItem) { 835 listItem.add(colsItem); 836 } 837 } 838 839 if (allowSortingByColumn) { 840 JMenu sortItem = new JMenu(NbBundle.getBundle(NodeTableModel.class).getString("LBL_SortMenu")); 841 JRadioButtonMenuItem noSortItem = new JRadioButtonMenuItem( 842 NbBundle.getBundle(NodeTableModel.class).getString("LBL_NoSort"), 843 !getSortedNodeTreeModel().isSortingActive() 844 ); 845 noSortItem.addActionListener( 846 new ActionListener() { 847 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 848 noSorting(); 849 } 850 } 851 ); 852 sortItem.add(noSortItem); 853 854 int visibleComparable = 0; 855 JRadioButtonMenuItem colItem; 856 857 if (treeColumnProperty.isComparable()) { 858 visibleComparable++; 859 colItem = new JRadioButtonMenuItem(treeTable.getColumnName(0), treeColumnProperty.isSortingColumn()); 860 colItem.setHorizontalTextPosition(SwingConstants.LEFT); 861 colItem.addActionListener( 862 new ActionListener() { 863 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 864 setSortingColumn(-1); 865 } 866 } 867 ); 868 sortItem.add(colItem); 869 } 870 871 for (int i = 0; i < tableModel.getColumnCount(); i++) { 872 if (tableModel.isComparableColumn(i)) { 873 visibleComparable++; 874 colItem = new JRadioButtonMenuItem( 875 tableModel.getColumnName(i), 876 tableModel.isSortingColumnEx(tableModel.translateVisibleColumnIndex(i)) 877 ); 878 colItem.setHorizontalTextPosition(SwingConstants.LEFT); 879 880 final int index = tableModel.translateVisibleColumnIndex(i); 881 colItem.addActionListener( 882 new ActionListener() { 883 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 884 setSortingColumn(index); 885 } 886 } 887 ); 888 sortItem.add(colItem); 889 } 890 } 891 892 for (int i = 0; i < tableModel.getColumnCountEx(); i++) { 894 if (tableModel.isComparableColumnEx(i) && !tableModel.isVisibleColumnEx(i)) { 895 visibleComparable++; 896 colItem = new JRadioButtonMenuItem(tableModel.getColumnNameEx(i), tableModel.isSortingColumnEx(i)); 897 colItem.setHorizontalTextPosition(SwingConstants.LEFT); 898 899 final int index = i; 900 colItem.addActionListener( 901 new ActionListener() { 902 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 903 setSortingColumn(index); 904 } 905 } 906 ); 907 sortItem.add(colItem); 908 } 909 } 910 911 if (visibleComparable > 0) { 912 sortItem.addSeparator(); 913 914 boolean current_sort; 915 916 if (treeColumnProperty.isSortingColumn()) { 917 current_sort = treeColumnProperty.isSortOrderDescending(); 918 } else { 919 current_sort = tableModel.isSortOrderDescending(); 920 } 921 922 JRadioButtonMenuItem ascItem = new JRadioButtonMenuItem( 923 NbBundle.getBundle(NodeTableModel.class).getString("LBL_Ascending"), !current_sort 924 ); 925 ascItem.setHorizontalTextPosition(SwingConstants.LEFT); 926 ascItem.addActionListener( 927 new ActionListener() { 928 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 929 setSortingOrder(true); 930 } 931 } 932 ); 933 sortItem.add(ascItem); 934 935 JRadioButtonMenuItem descItem = new JRadioButtonMenuItem( 936 NbBundle.getBundle(NodeTableModel.class).getString("LBL_Descending"), current_sort 937 ); 938 descItem.setHorizontalTextPosition(SwingConstants.LEFT); 939 descItem.addActionListener( 940 new ActionListener() { 941 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 942 setSortingOrder(false); 943 } 944 } 945 ); 946 sortItem.add(descItem); 947 948 if (!getSortedNodeTreeModel().isSortingActive()) { 949 ascItem.setEnabled(false); 950 descItem.setEnabled(false); 951 } 952 953 listItem.add(sortItem); 954 } 955 } 956 957 if (allowHideColumns) { 958 JMenuItem visItem = new JMenuItem(NbBundle.getBundle(NodeTableModel.class).getString("LBL_ChangeColumns")); 959 visItem.addActionListener( 960 new ActionListener() { 961 public void actionPerformed(java.awt.event.ActionEvent actionEvent) { 962 selectVisibleColumns(); 963 } 964 } 965 ); 966 967 listItem.add(visItem); 968 } 969 970 return listItem; 971 } 972 973 975 private void setSortingColumn(int index) { 976 tableModel.setSortingColumnEx(index); 977 978 if (index != -1) { 979 getSortedNodeTreeModel().setSortedByProperty( 980 tableModel.propertyForColumnEx(index), !tableModel.isSortOrderDescending() 981 ); 982 treeColumnProperty.setSortingColumn(false); 983 } else { 984 getSortedNodeTreeModel().setSortedByName(true, !treeColumnProperty.isSortOrderDescending()); 985 treeColumnProperty.setSortingColumn(true); 986 } 987 988 treeTable.getTableHeader().repaint(); 990 } 991 992 private void noSorting() { 993 tableModel.setSortingColumnEx(-1); 994 getSortedNodeTreeModel().setNoSorting(); 995 treeColumnProperty.setSortingColumn(false); 996 997 treeTable.getTableHeader().repaint(); 999 } 1000 1001 1003 private void setSortingOrder(boolean ascending) { 1004 if (treeColumnProperty.isSortingColumn()) { 1005 treeColumnProperty.setSortOrderDescending(!ascending); 1006 } else { 1007 tableModel.setSortOrderDescending(!ascending); 1008 } 1009 1010 getSortedNodeTreeModel().setSortOrder(ascending); 1011 1012 treeTable.getTableHeader().repaint(); 1014 } 1015 1016 private synchronized SortedNodeTreeModel getSortedNodeTreeModel() { 1017 if (sortedNodeTreeModel == null) { 1018 sortedNodeTreeModel = new SortedNodeTreeModel(); 1019 } 1020 1021 return sortedNodeTreeModel; 1022 } 1023 1024 1028 private class AccessibleTreeTableView extends AccessibleJScrollPane { 1029 AccessibleTreeTableView() { 1030 } 1031 1032 public void setAccessibleName(String accessibleName) { 1033 super.setAccessibleName(accessibleName); 1034 1035 if (treeTable != null) { 1036 treeTable.getAccessibleContext().setAccessibleName(accessibleName); 1037 } 1038 } 1039 1040 public void setAccessibleDescription(String accessibleDescription) { 1041 super.setAccessibleDescription(accessibleDescription); 1042 1043 if (treeTable != null) { 1044 treeTable.getAccessibleContext().setAccessibleDescription(accessibleDescription); 1045 } 1046 } 1047 } 1048 1049 1051 private final class ScrollListener extends ComponentAdapter implements PropertyChangeListener , ChangeListener { 1052 boolean movecorrection = false; 1053 1054 ScrollListener() { 1055 } 1056 1057 public void propertyChange(PropertyChangeEvent evt) { 1059 if (((TreeTable) treeTable).getTreeColumnIndex() == -1) { 1060 return; 1061 } 1062 1063 if ("width".equals(evt.getPropertyName())) { 1065 if (!treeTable.equals(evt.getSource())) { 1066 Dimension dim = hScrollBar.getPreferredSize(); 1067 dim.width = treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()) 1068 .getWidth(); 1069 hScrollBar.setPreferredSize(dim); 1070 hScrollBar.revalidate(); 1071 hScrollBar.repaint(); 1072 } 1073 1074 revalidateScrollBar(); 1075 } else if ("positionX".equals(evt.getPropertyName())) { revalidateScrollBar(); 1077 } else if ("treeColumnIndex".equals(evt.getPropertyName())) { treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()) 1079 .addPropertyChangeListener(listener); 1080 } else if ("column_moved".equals(evt.getPropertyName())) { 1082 int from = ((Integer ) evt.getOldValue()).intValue(); 1083 int to = ((Integer ) evt.getNewValue()).intValue(); 1084 1085 if ((from == 0) || (to == 0)) { 1086 if (movecorrection) { 1087 movecorrection = false; 1088 } else { 1089 movecorrection = true; 1090 1091 treeTable.getColumnModel().moveColumn(to, from); 1093 } 1094 1095 return; 1096 } 1097 1098 treeTable.getTableHeader().getColumnModel().getColumn(from).setModelIndex(from); 1100 treeTable.getTableHeader().getColumnModel().getColumn(to).setModelIndex(to); 1101 tableModel.moveColumn(from - 1, to - 1); 1102 } 1103 } 1104 1105 public void componentResized(ComponentEvent e) { 1107 revalidateScrollBar(); 1108 } 1109 1110 public void stateChanged(ChangeEvent evt) { 1112 int value = hScrollBar.getModel().getValue(); 1113 ((TreeTable) treeTable).setPositionX(value); 1114 } 1115 1116 private void revalidateScrollBar() { 1117 if (!isDisplayable()) { 1118 return; 1119 } 1120 1121 if ( 1122 (treeTable.getColumnModel().getColumnCount() > 0) && 1123 (((TreeTable) treeTable).getTreeColumnIndex() >= 0) 1124 ) { 1125 int extentWidth = treeTable.getColumnModel().getColumn(((TreeTable) treeTable).getTreeColumnIndex()) 1126 .getWidth(); 1127 int maxWidth = tree.getPreferredSize().width; 1128 int extentHeight = scrollPane.getViewport().getSize().height; 1129 int maxHeight = tree.getPreferredSize().height; 1130 int positionX = ((TreeTable) treeTable).getPositionX(); 1131 1132 int value = Math.max(0, Math.min(positionX, maxWidth - extentWidth)); 1133 1134 boolean hsbvisible = hScrollBar.isVisible(); 1135 boolean vsbvisible = scrollPane.getVerticalScrollBar().isVisible(); 1136 int hsbheight = hsbvisible ? hScrollBar.getHeight() : 0; 1137 int vsbwidth = scrollPane.getVerticalScrollBar().getWidth(); 1138 1139 hScrollBar.setValues(value, extentWidth, 0, maxWidth); 1140 1141 if ( 1142 hideHScrollBar || (maxWidth <= extentWidth) || 1143 (vsbvisible && 1144 ((maxHeight <= (extentHeight + hsbheight)) && (maxWidth <= (extentWidth + vsbwidth)))) 1145 ) { 1146 hScrollBar.setVisible(false); 1147 } else { 1148 hScrollBar.setVisible(true); 1149 } 1150 } 1151 } 1152 } 1153 1154 1157 private static final class CompoundScrollPane extends JPanel implements Scrollable { 1158 CompoundScrollPane() { 1159 } 1160 1161 public void setBorder(Border b) { 1162 } 1164 1165 public boolean getScrollableTracksViewportWidth() { 1166 return true; 1167 } 1168 1169 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 1170 return 10; 1171 } 1172 1173 public boolean getScrollableTracksViewportHeight() { 1174 return true; 1175 } 1176 1177 public Dimension getPreferredScrollableViewportSize() { 1178 return this.getPreferredSize(); 1179 } 1180 1181 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { 1182 return 10; 1183 } 1184 } 1185 1186 1188 private class DefaultTreeAction implements ActionListener { 1189 DefaultTreeAction() { 1190 } 1191 1192 1195 public void actionPerformed(ActionEvent e) { 1196 if (treeTable.getSelectedColumn() != ((TreeTable) treeTable).getTreeColumnIndex()) { 1197 return; 1198 } 1199 1200 Node[] nodes = manager.getSelectedNodes(); 1201 1202 if (nodes.length == 1) { 1203 Action a = nodes[0].getPreferredAction(); 1204 1205 if (a != null) { 1206 if (a.isEnabled()) { 1207 a.actionPerformed(new ActionEvent(nodes[0], ActionEvent.ACTION_PERFORMED, "")); } else { 1209 Toolkit.getDefaultToolkit().beep(); 1210 } 1211 } 1212 } 1213 } 1214 } 1215 1216 1218 private class SortedNodeTreeModel extends NodeTreeModel { 1219 private Node.Property sortedByProperty; 1220 private boolean sortAscending = true; 1221 private Comparator <VisualizerNode> rowComparator; 1222 private boolean sortedByName = false; 1223 private SortingTask sortingTask = null; 1224 1225 SortedNodeTreeModel() { 1226 } 1227 1228 void setNoSorting() { 1229 setSortedByProperty(null); 1230 setSortedByName(false); 1231 sortingChanged(); 1232 } 1233 1234 boolean isSortingActive() { 1235 return ((sortedByProperty != null) || sortedByName); 1236 } 1237 1238 void setSortedByProperty(Node.Property prop) { 1239 if (sortedByProperty == prop) { 1240 return; 1241 } 1242 1243 sortedByProperty = prop; 1244 1245 if (prop == null) { 1246 rowComparator = null; 1247 } else { 1248 sortedByName = false; 1249 } 1250 1251 sortingChanged(); 1252 } 1253 1254 void setSortedByProperty(Node.Property prop, boolean ascending) { 1255 if ((sortedByProperty == prop) && (ascending == sortAscending)) { 1256 return; 1257 } 1258 1259 sortedByProperty = prop; 1260 sortAscending = ascending; 1261 1262 if (prop == null) { 1263 rowComparator = null; 1264 } else { 1265 sortedByName = false; 1266 } 1267 1268 sortingChanged(); 1269 } 1270 1271 void setSortedByName(boolean sorted, boolean ascending) { 1272 if ((sortedByName == sorted) && (ascending == sortAscending)) { 1273 return; 1274 } 1275 1276 sortedByName = sorted; 1277 sortAscending = ascending; 1278 1279 if (sortedByName) { 1280 sortedByProperty = null; 1281 } 1282 1283 sortingChanged(); 1284 } 1285 1286 void setSortedByName(boolean sorted) { 1287 sortedByName = sorted; 1288 1289 if (sortedByName) { 1290 sortedByProperty = null; 1291 } 1292 1293 sortingChanged(); 1294 } 1295 1296 void setSortOrder(boolean ascending) { 1297 if (ascending == sortAscending) { 1298 return; 1299 } 1300 1301 sortAscending = ascending; 1302 sortingChanged(); 1303 } 1304 1305 private Node.Property getNodeProperty(Node node, Node.Property prop) { 1306 Node.PropertySet[] propsets = node.getPropertySets(); 1307 1308 for (int i = 0, n = propsets.length; i < n; i++) { 1309 Node.Property[] props = propsets[i].getProperties(); 1310 1311 for (int j = 0, m = props.length; j < m; j++) { 1312 if (props[j].equals(prop)) { 1313 return props[j]; 1314 } 1315 } 1316 } 1317 1318 return null; 1319 } 1320 1321 synchronized Comparator <VisualizerNode> getRowComparator() { 1322 if (rowComparator == null) { 1323 rowComparator = new Comparator <VisualizerNode>() { 1324 1325 public int compare(VisualizerNode o1, VisualizerNode o2) { 1326 if (o1 == o2) { 1327 return 0; 1328 } 1329 Node n1 = o1.node; 1330 Node n2 = o2.node; 1331 1332 if ((n1 == null) && (n2 == null)) { 1333 return 0; 1334 } 1335 if (n1 == null) { 1336 return 1; 1337 } 1338 if (n2 == null) { 1339 return -1; 1340 } 1341 if ((n1.getParentNode() == null) || 1342 (n2.getParentNode() == null)) { 1343 Logger.getAnonymousLogger().warning("TTV.compare: Node " + 1345 n1 + " or " + n2 + 1346 " has no parent!"); 1347 return 0; 1348 } 1349 if (!(n1.getParentNode().equals(n2.getParentNode()))) { 1350 Logger.getAnonymousLogger().warning("TTV.compare: Nodes " + 1352 n1 + " and " + 1353 n2 + 1354 " has different parent!"); 1355 return 0; 1356 } 1357 int res = 0; 1358 1359 if (sortedByName) { 1360 res = n1.getDisplayName().compareTo(n2.getDisplayName()); 1361 return sortAscending ? res 1362 : (-res); 1363 } 1364 Property p1 = getNodeProperty(n1, sortedByProperty); 1365 Property p2 = getNodeProperty(n2, sortedByProperty); 1366 1367 if ((p1 == null) && (p2 == null)) { 1368 return 0; 1369 } 1370 try { 1371 if (p1 == null) { 1372 res = -1; 1373 } else if (p2 == null) { 1374 res = 1; 1375 } else { 1376 Object v1 = p1.getValue(); 1377 Object v2 = p2.getValue(); 1378 1379 if ((v1 == null) && (v2 == null)) { 1380 return 0; 1381 } else if (v1 == null) { 1382 res = -1; 1383 } else if (v2 == null) { 1384 res = 1; 1385 } else { 1386 if ((v1.getClass() != v2.getClass()) || 1387 !(v1 instanceof Comparable )) { 1388 v1 = v1.toString(); 1389 v2 = v2.toString(); 1390 } 1391 res = ((Comparable ) v1).compareTo(v2); 1392 } 1393 } 1394 return sortAscending ? res 1395 : (-res); 1396 } 1397 catch (Exception ex) { 1398 Logger.getLogger(TreeTableView.class.getName()).log(Level.WARNING, null, ex); 1399 return 0; 1400 } 1401 } 1402 }; 1403 } 1404 1405 return rowComparator; 1406 } 1407 1408 void sortChildren(VisualizerNode parent, boolean synchronous) { 1409 if (synchronous) { 1414 synchronized (this) { 1415 if (sortingTask != null) { 1416 sortingTask.remove(parent); 1417 1418 if (sortingTask.isEmpty()) { 1419 sortingTask = null; 1420 } 1421 } 1422 } 1423 1424 doSortChildren(parent); 1425 } else { 1426 synchronized (this) { 1427 if (sortingTask == null) { 1428 sortingTask = new SortingTask(); 1429 SwingUtilities.invokeLater(sortingTask); 1430 } 1431 } 1432 1433 sortingTask.add(parent); 1434 } 1435 } 1436 1437 void doSortChildren(VisualizerNode parent) { 1438 if (isSortingActive()) { 1439 final Comparator <VisualizerNode> comparator = getRowComparator(); 1440 1441 if ((comparator != null) || (parent != null)) { 1442 parent.reorderChildren(comparator); 1443 } 1444 } else { 1445 parent.naturalOrder(); 1446 } 1447 } 1448 1449 void sortingChanged() { 1450 TreeNode tn = (TreeNode) (this.getRoot()); 1453 java.util.List <TreePath> list = new ArrayList <TreePath>(); 1454 Enumeration <TreePath> en = TreeTableView.this.tree.getExpandedDescendants(new TreePath(tn)); 1455 1456 while ((en != null) && en.hasMoreElements()) { 1457 TreePath path = en.nextElement(); 1458 1459 sortChildren((VisualizerNode) path.getLastPathComponent(), true); 1461 list.add(path); 1462 } 1463 1464 for (int i = 0; i < list.size(); i++) { 1466 TreeTableView.this.tree.expandPath(list.get(i)); 1467 } 1468 } 1469 1470 String getRootDescription() { 1471 if (getRoot() instanceof VisualizerNode) { 1472 return ((VisualizerNode) getRoot()).getShortDescription(); 1477 } 1478 1479 return ""; } 1481 1482 public void nodesWereInserted(TreeNode node, int[] childIndices) { 1484 super.nodesWereInserted(node, childIndices); 1485 1486 if (node instanceof VisualizerNode && isSortingActive()) { 1487 sortChildren((VisualizerNode) node, false); 1488 } 1489 } 1490 1491 public void nodesChanged(TreeNode node, int[] childIndices) { 1493 super.nodesChanged(node, childIndices); 1494 1495 if ((node != null) && (childIndices != null) && isSortingActive()) { 1496 sortChildren((VisualizerNode) node, false); 1497 } 1498 } 1499 1500 public void setRoot(TreeNode root) { 1502 super.setRoot(root); 1503 1504 if (root instanceof VisualizerNode && isSortingActive()) { 1505 sortChildren((VisualizerNode) root, false); 1506 } 1507 } 1508 1509 private class SortingTask implements Runnable { 1510 private HashSet <VisualizerNode> toSort = new HashSet <VisualizerNode>(); 1511 1512 public synchronized void add(VisualizerNode parent) { 1513 toSort.add(parent); 1514 } 1515 1516 public synchronized void remove(VisualizerNode parent) { 1517 toSort.remove(parent); 1518 } 1519 1520 public synchronized boolean isEmpty() { 1521 return toSort.isEmpty(); 1522 } 1523 1524 public void run() { 1525 synchronized (SortedNodeTreeModel.this) { 1526 SortedNodeTreeModel.this.sortingTask = null; 1527 } 1528 1529 for (Iterator <VisualizerNode> i = toSort.iterator(); i.hasNext();) { 1530 VisualizerNode curr = i.next(); 1531 SortedNodeTreeModel.this.doSortChildren(curr); 1532 } 1533 } 1534 } 1535 } 1536 1537 1539 private class SortingHeaderRenderer extends DefaultTableCellRenderer { 1540 SortingHeaderRenderer() { 1541 } 1542 1543 1544 public Component getTableCellRendererComponent( 1545 JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column 1546 ) { 1547 Component comp = defaultHeaderRenderer.getTableCellRendererComponent( 1548 table, value, isSelected, hasFocus, row, column 1549 ); 1550 1551 if (comp instanceof JLabel) { 1552 if ((column == 0) && treeColumnProperty.isSortingColumn()) { 1553 ((JLabel) comp).setIcon(getProperIcon(treeColumnProperty.isSortOrderDescending())); 1554 ((JLabel) comp).setHorizontalTextPosition(SwingConstants.LEFT); 1555 1556 comp.setFont(new Font(comp.getFont().getName(), Font.BOLD, comp.getFont().getSize())); 1558 } else if ((column != 0) && ((tableModel.getVisibleSortingColumn() + 1) == column)) { 1559 ((JLabel) comp).setIcon(getProperIcon(tableModel.isSortOrderDescending())); 1560 ((JLabel) comp).setHorizontalTextPosition(SwingConstants.LEFT); 1561 1562 comp.setFont(new Font(comp.getFont().getName(), Font.BOLD, comp.getFont().getSize())); 1564 } else { 1565 ((JLabel) comp).setIcon(null); 1566 } 1567 } 1568 1569 return comp; 1570 } 1571 1572 private ImageIcon getProperIcon(boolean descending) { 1573 if (descending) { 1574 return new ImageIcon(org.openide.util.Utilities.loadImage(SORT_DESC_ICON)); 1575 } else { 1576 return new ImageIcon(org.openide.util.Utilities.loadImage(SORT_ASC_ICON)); 1577 } 1578 } 1579 } 1580 1582 private static class TreeColumnProperty { 1583 private Property p = null; 1584 1585 TreeColumnProperty() { 1586 } 1587 1588 void setProperty(Property p) { 1589 this.p = p; 1590 } 1591 1592 boolean isComparable() { 1593 if (p == null) { 1594 return false; 1595 } 1596 1597 Object o = p.getValue(NodeTableModel.ATTR_COMPARABLE_COLUMN); 1598 1599 if ((o != null) && o instanceof Boolean ) { 1600 return ((Boolean ) o).booleanValue(); 1601 } 1602 1603 return false; 1604 } 1605 1606 boolean isSortingColumn() { 1607 if (p == null) { 1608 return false; 1609 } 1610 1611 Object o = p.getValue(NodeTableModel.ATTR_SORTING_COLUMN); 1612 1613 if ((o != null) && o instanceof Boolean ) { 1614 return ((Boolean ) o).booleanValue(); 1615 } 1616 1617 return false; 1618 } 1619 1620 void setSortingColumn(boolean sorting) { 1621 if (p == null) { 1622 return; 1623 } 1624 1625 p.setValue(NodeTableModel.ATTR_SORTING_COLUMN, sorting ? Boolean.TRUE : Boolean.FALSE); 1626 } 1627 1628 boolean isSortOrderDescending() { 1629 if (p == null) { 1630 return false; 1631 } 1632 1633 Object o = p.getValue(NodeTableModel.ATTR_DESCENDING_ORDER); 1634 1635 if ((o != null) && o instanceof Boolean ) { 1636 return ((Boolean ) o).booleanValue(); 1637 } 1638 1639 return false; 1640 } 1641 1642 void setSortOrderDescending(boolean descending) { 1643 if (p == null) { 1644 return; 1645 } 1646 1647 p.setValue(NodeTableModel.ATTR_DESCENDING_ORDER, descending ? Boolean.TRUE : Boolean.FALSE); 1648 } 1649 } 1650 1651 1732} 1733 | Popular Tags |