1 19 20 24 package org.netbeans.modules.tasklist.usertasks.treetable; 25 26 import javax.swing.JTextField ; 27 import javax.swing.DefaultCellEditor ; 28 import java.awt.Component ; 29 import java.awt.Graphics ; 30 import java.awt.Rectangle ; 31 import java.awt.event.InputEvent ; 32 import java.awt.event.KeyEvent ; 33 import java.awt.event.MouseEvent ; 34 import java.awt.event.MouseListener ; 35 import java.io.Serializable ; 36 import java.util.ArrayList ; 37 import java.util.Enumeration ; 38 import java.util.EventObject ; 39 import java.util.List ; 40 import java.util.logging.Level ; 41 import javax.swing.Icon ; 42 import javax.swing.InputMap ; 43 import javax.swing.JTable ; 44 import javax.swing.JTree ; 45 import javax.swing.KeyStroke ; 46 import javax.swing.ListSelectionModel ; 47 import javax.swing.LookAndFeel ; 48 import javax.swing.UIManager ; 49 import javax.swing.border.Border ; 50 import javax.swing.event.ChangeEvent ; 51 import javax.swing.event.ChangeListener ; 52 import javax.swing.event.ListSelectionEvent ; 53 import javax.swing.event.ListSelectionListener ; 54 import javax.swing.table.TableCellEditor ; 55 import javax.swing.table.TableCellRenderer ; 56 import javax.swing.table.TableColumn ; 57 import javax.swing.table.TableColumnModel ; 58 import javax.swing.table.TableModel ; 59 import javax.swing.tree.DefaultMutableTreeNode ; 60 import javax.swing.tree.DefaultTreeCellRenderer ; 61 import javax.swing.tree.DefaultTreeModel ; 62 import javax.swing.tree.DefaultTreeSelectionModel ; 63 import javax.swing.tree.TreeCellRenderer ; 64 import javax.swing.tree.TreeModel ; 65 import javax.swing.tree.TreePath ; 66 import org.netbeans.modules.tasklist.core.table.SortingModel; 67 import org.netbeans.modules.tasklist.usertasks.treetable.*; 68 import org.netbeans.modules.tasklist.usertasks.util.UTUtils; 69 70 71 72 82 public class TreeTable extends JTable { 83 86 public static final class ColumnsConfig extends 87 org.netbeans.modules.tasklist.core.table.ColumnsConfig { 88 public static final long serialVersionUID = 2L; 89 } 90 91 95 private static final class ExpandedNodesAndSelection { 96 public static final long serialVersionUID = 1L; 97 98 99 public TreePath [] selection; 100 101 102 public TreePath [] expandedNodes; 103 } 104 105 private static final long serialVersionUID = 1; 106 107 108 protected TreeTableCellRenderer tree; 109 private TreeTableModel treeTableModel; 110 private SortingModel sortingModel; 111 private boolean paintDisabled; 112 113 public TreeTable(TreeTableModel treeTableModel) { 114 super(); 115 putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); 117 tree = new TreeTableCellRenderer( 122 new DefaultTreeModel (new DefaultMutableTreeNode ())); 123 124 setTreeTableModel(treeTableModel); 126 127 ListToTreeSelectionModelWrapper selectionWrapper = 129 new ListToTreeSelectionModelWrapper(); 130 tree.setSelectionModel(selectionWrapper); 131 setSelectionModel(selectionWrapper.getListSelectionModel()); 132 133 setDefaultRenderer(TreeTableModel.class, tree); 135 setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor()); 136 137 setShowGrid(false); 139 140 143 if (tree.getRowHeight() < 1) { 146 setRowHeight(18); 148 } 149 150 this.sortingModel = new SortingModel(); 151 152 InputMap imp2 = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 153 154 imp2.put(KeyStroke.getKeyStroke("control C"), "none"); imp2.put(KeyStroke.getKeyStroke("control V"), "none"); imp2.put(KeyStroke.getKeyStroke("control X"), "none"); imp2.put(KeyStroke.getKeyStroke("COPY"), "none"); imp2.put(KeyStroke.getKeyStroke("PASTE"), "none"); imp2.put(KeyStroke.getKeyStroke("CUT"), "none"); imp2.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), 163 "expand"); imp2.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), 165 "collapse"); 167 getActionMap().put("expand", new ExpandCollapseAction(true, this)); 169 getActionMap().put("collapse", new ExpandCollapseAction(false, this)); 171 172 getSortingModel().addChangeListener(new ChangeListener () { 174 public void stateChanged(ChangeEvent e) { 175 Object es = getExpandedNodesAndSelection(); 176 177 getTreeTableModel().sort(getSortingModel()); 178 179 setExpandedNodesAndSelection(es); 180 } 181 }); 182 } 183 184 189 public TreePath getSelectedPath() { 190 int row = getSelectedRow(); 191 if (row < 0) 192 return null; 193 194 return tree.getPathForRow(row); 195 } 196 197 203 public boolean isTreeColumn(int column) { 204 return getColumnClass(column) == TreeTableModel.class; 205 } 206 207 212 public TreePath [] getSelectedPaths() { 213 int[] rows = getSelectedRows(); 214 TreePath [] paths = new TreePath [rows.length]; 215 for (int i = 0; i < rows.length; i++) { 216 paths[i] = tree.getPathForRow(rows[i]); 217 } 218 return paths; 219 } 220 221 228 public Object getExpandedNodesAndSelection() { 229 TreeTable.ExpandedNodesAndSelection ret = 230 new TreeTable.ExpandedNodesAndSelection(); 231 232 Enumeration <TreePath > en = tree.getExpandedDescendants( 233 new TreePath (getTreeTableModel().getRoot())); 234 if (en != null) { 235 List <TreePath > exp = new ArrayList <TreePath >(); 236 while (en.hasMoreElements()) { 237 exp.add(en.nextElement()); 238 } 239 ret.expandedNodes = exp.toArray(new TreePath [exp.size()]); 240 } else { 241 ret.expandedNodes = new TreePath [0]; 242 } 243 244 int[] selRows = getSelectedRows(); 245 ret.selection = new TreePath [selRows.length]; 246 for (int i = 0; i < selRows.length; i++) { 247 ret.selection[i] = tree.getPathForRow(selRows[i]); 248 } 249 250 return ret; 251 } 252 253 260 public void setExpandedNodesAndSelection(Object obj) { 261 TreeTable.ExpandedNodesAndSelection es = 262 (TreeTable.ExpandedNodesAndSelection) obj; 263 264 for (int i = 0; i < es.expandedNodes.length; i++) { 266 tree.expandPath(es.expandedNodes[i]); 267 } 268 269 for (int i = 0; i < es.selection.length; i++) { 271 int row = tree.getRowForPath(es.selection[i]); 272 getSelectionModel().addSelectionInterval(row, row); 273 } 274 } 275 276 279 public void expandAll() { 280 TreePath tp = new TreePath (tree.getModel().getRoot()); 281 expandAllUnder(tp); 282 } 283 284 287 public void collapseAll() { 288 Object root = tree.getModel().getRoot(); 289 TreePath rootPath = new TreePath (root); 290 if (getTree().isRootVisible()) 291 collapseAllUnder(rootPath); 292 else { 293 int n = tree.getModel().getChildCount(root); 294 for (int i = 0; i < n; i++) { 295 collapseAllUnder(rootPath.pathByAddingChild( 296 tree.getModel().getChild(root, i))); 297 } 298 } 299 } 300 301 306 public void expandAllPath(TreePath tp) { 307 while (tp != null) { 308 tree.expandPath(tp); 309 tp = tp.getParentPath(); 310 } 311 } 312 313 318 public void expandPath(TreePath tp) { 319 tree.expandPath(tp); 320 } 321 322 327 public void select(TreePath path) { 328 int row = this.getRowForPath(path); 329 if (row >= 0) 330 this.getSelectionModel().setSelectionInterval(row, row); 331 } 332 333 338 public void select(TreePath [] path) { 339 getSelectionModel().clearSelection(); 340 for (int i = 0; i < path.length; i++) { 341 int row = this.getRowForPath(path[i]); 342 if (row >= 0) 343 getSelectionModel().addSelectionInterval(row, row); 344 } 345 } 346 347 352 public void scrollTo(TreePath path) { 353 int row = this.getRowForPath(path); 354 if (row > 0) { 355 Rectangle r = this.getCellRect(row, 0, true); 356 this.scrollRectToVisible(r); 357 } 358 } 359 360 365 public void expandAllUnder(TreePath tp) { 366 tree.expandPath(tp); 367 Object last = tp.getLastPathComponent(); 368 for (int i = 0; i < tree.getModel().getChildCount(last); i++) { 369 Object child = tree.getModel().getChild(last, i); 370 expandAllUnder(tp.pathByAddingChild(child)); 371 } 372 } 373 374 379 public void collapseAllUnder(TreePath tp) { 380 if (!tree.hasBeenExpanded(tp)) 381 return; 382 383 Object last = tp.getLastPathComponent(); 384 for (int i = 0; i < tree.getModel().getChildCount(last); i++) { 385 Object child = tree.getModel().getChild(last, i); 386 collapseAllUnder(tp.pathByAddingChild(child)); 387 } 388 tree.collapsePath(tp); 389 } 390 391 396 public void setSortingModel(SortingModel sm) { 397 SortingModel old = this.sortingModel; 398 this.sortingModel = sm; 399 firePropertyChange("sortingModel", old, sm); } 401 402 407 public SortingModel getSortingModel() { 408 return sortingModel; 409 } 410 411 416 public void setTreeTableModel(TreeTableModel treeTableModel) { 417 this.treeTableModel = treeTableModel; 418 if (getModel() instanceof TreeTableModelAdapter) 419 ((TreeTableModelAdapter) getModel()).unregister(); 420 super.setModel(new TreeTableModelAdapter(treeTableModel, tree)); 421 tree.setModel(treeTableModel); 422 } 423 424 429 public TreeTableModel getTreeTableModel() { 430 return treeTableModel; 431 } 432 433 438 public Object getNodeForRow(int row) { 439 TreePath tp = tree.getPathForRow(row); 440 return tp.getLastPathComponent(); 442 } 443 444 450 public int getRowForPath(TreePath path) { 451 return tree.getRowForPath(path); 452 } 453 454 459 public void updateUI() { 460 super.updateUI(); 461 if(tree != null) { 462 tree.updateUI(); 463 setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor()); 467 } 468 LookAndFeel.installColorsAndFont(this, "Tree.background", "Tree.foreground", "Tree.font"); } 473 474 478 private int realEditingRow() { 479 return editingRow; 480 } 481 482 489 public void sizeColumnsToFit(int resizingColumn) { 490 super.sizeColumnsToFit(resizingColumn); 491 if (getEditingColumn() != -1 && getColumnClass(editingColumn) == 492 TreeTableModel.class) { 493 Rectangle cellRect = getCellRect(realEditingRow(), 494 getEditingColumn(), false); 495 Component component = getEditorComponent(); 496 component.setBounds(cellRect); 497 component.validate(); 498 } 499 } 500 501 507 public boolean editCellAt(int row, int column, EventObject e){ 508 if (cellEditor != null && !cellEditor.stopCellEditing()) { 509 return false; 510 } 511 512 if (row < 0 || row >= getRowCount() || 513 column < 0 || column >= getColumnCount()) { 514 return false; 515 } 516 517 if (!isCellEditable(row, column)) { 518 TableCellEditor editor = getCellEditor(row, column); 519 if (editor != null) 520 editor.isCellEditable(e); 521 return false; 522 } 523 524 boolean retValue = super.editCellAt(row, column, e); 525 if (retValue && getColumnClass(column) == TreeTableModel.class) { 526 repaint(getCellRect(row, column, false)); 527 } 528 return retValue; 529 } 530 531 538 public int getEditingRow() { 539 return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 : 540 editingRow; 541 } 542 543 546 public void setRowHeight(int rowHeight) { 547 super.setRowHeight(rowHeight); 548 if (tree != null && tree.getRowHeight() != rowHeight) { 549 tree.setRowHeight(getRowHeight()); 550 } 551 } 552 553 556 public JTree getTree() { 557 return tree; 558 } 559 560 protected javax.swing.table.JTableHeader createDefaultTableHeader() { 561 return new SortableTableHeader(columnModel); 562 } 563 564 573 protected Serializable writeReplaceNode(Object node) { 574 return (Serializable ) node; 575 } 576 577 585 protected Object readResolveNode(Object parent, Object node) { 586 return node; 587 } 588 589 594 public ColumnsConfig getColumnsConfig() { 595 ColumnsConfig cc = new ColumnsConfig(); 596 597 TableColumnModel ctm = getColumnModel(); 598 assert ctm != null : "ctm == null"; 600 cc.columns = new int[ctm.getColumnCount()]; 601 cc.columnWidths = new int[ctm.getColumnCount()]; 602 for (int i = 0; i < ctm.getColumnCount(); i++) { 603 TableColumn c = ctm.getColumn(i); 604 cc.columns[i] = c.getModelIndex(); 605 cc.columnWidths[i] = c.getWidth(); 606 } 607 608 cc.sortedColumn = getSortingModel().getSortedColumn(); 609 cc.ascending = !getSortingModel().isSortOrderDescending(); 610 611 return cc; 612 } 613 614 619 public void setColumnsConfig(ColumnsConfig config) { 620 assert config != null : "config == null"; 622 this.createDefaultColumnsFromModel(); 623 624 ColumnsConfig cc = (ColumnsConfig) config; 625 626 ArrayList <TableColumn > newc = new ArrayList <TableColumn >(); 627 TableColumnModel tcm = getColumnModel(); 628 assert tcm != null : "tcm == null"; 630 for (int i = 0; i < cc.columns.length; i++) { 631 for (int j = 0; j < tcm.getColumnCount(); j++) { 632 TableColumn c = tcm.getColumn(j); 633 if (cc.columns[i] == c.getModelIndex()) { 634 newc.add(c); 635 tcm.removeColumn(c); 636 c.setPreferredWidth(cc.columnWidths[i]); 637 c.setWidth(cc.columnWidths[i]); 638 break; 639 } 640 } 641 } 642 while (tcm.getColumnCount() > 0) { 643 tcm.removeColumn(tcm.getColumn(0)); 644 } 645 for (int i = 0; i < newc.size(); i ++) { 646 tcm.addColumn(newc.get(i)); 647 } 648 } 649 650 655 public TreePath [] getExpandedNodes() { 656 Enumeration <TreePath > en = tree.getExpandedDescendants( 657 new TreePath (getTreeTableModel().getRoot())); 658 659 List <TreePath > paths = new ArrayList <TreePath >(); 660 if (en != null) { 661 while (en.hasMoreElements()) { 662 paths.add(en.nextElement()); 663 } 664 } 665 666 return paths.toArray(new TreePath [paths.size()]); 667 } 668 669 674 public void setExpandedNodes(TreePath [] n) { 675 for (int i = 0; i < n.length; i++) { 676 TreePath tp = n[i]; 677 expandPath(tp); 678 } 679 } 680 681 687 public Serializable writeReplaceExpandedNodes(TreePath [] n) { 688 List <Serializable > paths = new ArrayList <Serializable >(); 689 for (int i = 0; i < n.length; i++) { 690 paths.add(writeReplaceTreePath((TreePath ) n[i])); 691 } 692 693 return (Serializable ) paths; 694 } 695 696 702 public TreePath [] readResolveExpandedNodes(Object ser) { 703 if (ser == null) 704 return new TreePath [0]; 705 706 List <TreePath > ret = new ArrayList <TreePath >(); 707 List l = (List ) ser; 708 for (int i = 0; i < l.size(); i++) { 709 TreePath tp = readResolveTreePath(l.get(i)); 710 if (tp != null) 711 ret.add(tp); 712 } 713 714 return ret.toArray(new TreePath [ret.size()]); 715 } 716 717 723 public Serializable writeReplaceTreePath(TreePath tp) { 724 Object [] p = tp.getPath(); 725 p[0] = null; 726 for (int i = 1; i < p.length; i++) { 727 p[i] = writeReplaceNode(p[i]); 728 } 729 return p; 730 } 731 732 738 public TreePath readResolveTreePath(Object o) { 739 Object [] p = (Object []) o; 740 p[0] = getTreeTableModel().getRoot(); 741 for (int i = 1; i < p.length; i++) { 742 p[i] = readResolveNode(p[i - 1], p[i]); 743 if (p[i] == null) 744 return null; 745 } 746 return new TreePath (p); 747 } 748 749 756 public TreePath [] getExpandedNodesUnder(TreePath path) { 757 Enumeration <TreePath > en = tree.getExpandedDescendants(path); 758 759 List <TreePath > paths = new ArrayList <TreePath >(); 760 if (en != null) { 761 while (en.hasMoreElements()) { 762 paths.add(en.nextElement()); 763 } 764 } 765 766 return paths.toArray(new TreePath [paths.size()]); 767 } 768 769 775 776 779 public class TreeTableCellRenderer extends JTree implements 780 TableCellRenderer { 781 782 private static final long serialVersionUID = 1; 783 784 785 protected int visibleRow; 786 private Border border; 787 788 public TreeTableCellRenderer(TreeModel model) { 789 super(model); 790 } 791 792 796 public void updateUI() { 797 super.updateUI(); 798 TreeCellRenderer tcr = getCellRenderer(); 801 if (tcr instanceof DefaultTreeCellRenderer ) { 802 DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer )tcr); 803 dtcr.setTextSelectionColor(UIManager.getColor 808 ("Table.selectionForeground")); dtcr.setBackgroundSelectionColor(UIManager.getColor 810 ("Table.selectionBackground")); } 812 } 813 814 818 public void setRowHeight(int rowHeight) { 819 if (rowHeight > 0) { 820 super.setRowHeight(rowHeight); 821 if (TreeTable.this != null && 822 TreeTable.this.getRowHeight() != rowHeight) { 823 TreeTable.this.setRowHeight(getRowHeight()); 824 } 825 } 826 } 827 828 831 public void setBounds(int x, int y, int w, int h) { 832 super.setBounds(x, 0, w, TreeTable.this.getHeight()); 833 } 834 835 839 public void paint(Graphics g) { 840 845 846 g.translate(0, -visibleRow * getRowHeight()); 847 super.paint(g); 848 g.translate(0, visibleRow * getRowHeight()); 849 if (border != null) 850 border.paintBorder(this, g, 0, 0, getWidth(), 851 getRowHeight() - TreeTable.this.getRowMargin()); 852 } 853 854 907 910 public Component getTableCellRendererComponent(JTable table, 911 Object value, 912 boolean isSelected, 913 boolean hasFocus, 914 int row, int column) { 915 if (hasFocus) { 916 border = UIManager.getBorder("Table.focusCellHighlightBorder"); if (table.isCellEditable(row, column)) { 918 super.setForeground( UIManager.getColor("Table.focusCellForeground") ); super.setBackground( UIManager.getColor("Table.focusCellBackground") ); } 921 } else { 922 border = null; 923 } 924 925 if(isSelected) 926 setBackground(table.getSelectionBackground()); 927 else 928 setBackground(table.getBackground()); 929 930 visibleRow = row; 931 return this; 932 } 933 } 934 935 936 972 public class TreeTableCellEditor extends DefaultCellEditor { 973 public TreeTableCellEditor() { 974 super(new TreeTableTextField()); 975 } 976 977 985 public Component getTableCellEditorComponent(JTable table, 986 Object value, 987 boolean isSelected, 988 int r, int c) { 989 Component component = super.getTableCellEditorComponent 990 (table, value, isSelected, r, c); 991 JTree t = getTree(); 992 Rectangle bounds = t.getRowBounds(r); 993 int offset = bounds.x; 994 Rectangle cb = TreeTable.this.getCellRect(r, c, false); 995 offset += cb.x; 996 offset += 19; 997 ((TreeTableTextField)getComponent()).offset = offset; 998 return component; 999 } 1000 1001 1005 public boolean isCellEditable(EventObject e) { 1006 1009 if (e instanceof MouseEvent ) { 1010 MouseEvent me = (MouseEvent )e; 1011 1012 if (me.getModifiers() == 0 || 1019 me.getModifiers() == InputEvent.BUTTON1_MASK) { 1020 for (int counter = getColumnCount() - 1; counter >= 0; 1021 counter--) { 1022 if (getColumnClass(counter) == TreeTableModel.class) { 1023 MouseEvent newME = new MouseEvent 1024 (TreeTable.this.tree, me.getID(), 1025 me.getWhen(), me.getModifiers(), 1026 me.getX() - getCellRect(0, counter, true).x, 1027 me.getY(), me.getClickCount(), 1028 me.isPopupTrigger()); 1029 TreeTable.this.tree.dispatchEvent(newME); 1030 break; 1031 } 1032 } 1033 } 1034 1042 if (me.getClickCount() >= 3) { 1043 return true; 1044 } 1045 return false; 1046 } 1047 if (e == null) { 1048 return true; 1049 } 1050 return super.isCellEditable(e); 1051 } 1052 } 1053 1054 1059 static class TreeTableTextField extends JTextField { 1060 public int offset; 1061 1062 public void reshape(int x, int y, int w, int h) { 1063 int newX = Math.max(x, offset); 1064 super.reshape(newX, y, w - (newX - x), h); 1065 } 1066 } 1067 1068 1069 1075 class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { 1076 1077 private static final long serialVersionUID = 1; 1078 1079 1080 protected boolean updatingListSelectionModel; 1081 1082 public ListToTreeSelectionModelWrapper() { 1083 super(); 1084 getListSelectionModel().addListSelectionListener 1085 (createListSelectionListener()); 1086 } 1087 1088 1093 ListSelectionModel getListSelectionModel() { 1094 return listSelectionModel; 1095 } 1096 1097 1102 public void resetRowSelection() { 1103 if(!updatingListSelectionModel) { 1104 updatingListSelectionModel = true; 1105 try { 1106 super.resetRowSelection(); 1107 } 1108 finally { 1109 updatingListSelectionModel = false; 1110 } 1111 } 1112 } 1118 1119 1122 protected ListSelectionListener createListSelectionListener() { 1123 return new ListSelectionHandler(); 1124 } 1125 1126 1131 protected void updateSelectedPathsFromSelectedRows() { 1132 if(!updatingListSelectionModel) { 1133 updatingListSelectionModel = true; 1134 try { 1135 int min = listSelectionModel.getMinSelectionIndex(); 1138 int max = listSelectionModel.getMaxSelectionIndex(); 1139 1140 clearSelection(); 1141 if(min != -1 && max != -1) { 1142 for(int counter = min; counter <= max; counter++) { 1143 if(listSelectionModel.isSelectedIndex(counter)) { 1144 TreePath selPath = tree.getPathForRow 1145 (counter); 1146 1147 if(selPath != null) { 1148 addSelectionPath(selPath); 1149 } 1150 } 1151 } 1152 } 1153 } 1154 finally { 1155 updatingListSelectionModel = false; 1156 } 1157 } 1158 } 1159 1160 1164 class ListSelectionHandler implements ListSelectionListener { 1165 public void valueChanged(ListSelectionEvent e) { 1166 UTUtils.LOGGER.fine("changed"); updateSelectedPathsFromSelectedRows(); 1168 } 1169 } 1170 } 1171} 1172 | Popular Tags |