KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > tasklist > usertasks > treetable > TreeTable


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

19
20 /*
21  * this file is derived from the "Creating TreeTable" article at
22  * http://java.sun.com/products/jfc/tsc/articles/treetable2/index.html
23  */

24 package org.netbeans.modules.tasklist.usertasks.treetable;
25
26 import javax.swing.JTextField JavaDoc;
27 import javax.swing.DefaultCellEditor JavaDoc;
28 import java.awt.Component JavaDoc;
29 import java.awt.Graphics JavaDoc;
30 import java.awt.Rectangle JavaDoc;
31 import java.awt.event.InputEvent JavaDoc;
32 import java.awt.event.KeyEvent JavaDoc;
33 import java.awt.event.MouseEvent JavaDoc;
34 import java.awt.event.MouseListener JavaDoc;
35 import java.io.Serializable JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.Enumeration JavaDoc;
38 import java.util.EventObject JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.logging.Level JavaDoc;
41 import javax.swing.Icon JavaDoc;
42 import javax.swing.InputMap JavaDoc;
43 import javax.swing.JTable JavaDoc;
44 import javax.swing.JTree JavaDoc;
45 import javax.swing.KeyStroke JavaDoc;
46 import javax.swing.ListSelectionModel JavaDoc;
47 import javax.swing.LookAndFeel JavaDoc;
48 import javax.swing.UIManager JavaDoc;
49 import javax.swing.border.Border JavaDoc;
50 import javax.swing.event.ChangeEvent JavaDoc;
51 import javax.swing.event.ChangeListener JavaDoc;
52 import javax.swing.event.ListSelectionEvent JavaDoc;
53 import javax.swing.event.ListSelectionListener JavaDoc;
54 import javax.swing.table.TableCellEditor JavaDoc;
55 import javax.swing.table.TableCellRenderer JavaDoc;
56 import javax.swing.table.TableColumn JavaDoc;
57 import javax.swing.table.TableColumnModel JavaDoc;
58 import javax.swing.table.TableModel JavaDoc;
59 import javax.swing.tree.DefaultMutableTreeNode JavaDoc;
60 import javax.swing.tree.DefaultTreeCellRenderer JavaDoc;
61 import javax.swing.tree.DefaultTreeModel JavaDoc;
62 import javax.swing.tree.DefaultTreeSelectionModel JavaDoc;
63 import javax.swing.tree.TreeCellRenderer JavaDoc;
64 import javax.swing.tree.TreeModel JavaDoc;
65 import javax.swing.tree.TreePath JavaDoc;
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 /**
73  * This example shows how to create a simple JTreeTable component,
74  * by using a JTree as a renderer (and editor) for the cells in a
75  * particular column in the JTable.
76  *
77  * @version 1.2 10/27/98
78  *
79  * @author Philip Milne
80  * @author Scott Violet
81  */

82 public class TreeTable extends JTable JavaDoc {
83     /**
84      * Columns configuration
85      */

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     /**
92      * Expanded nodes and selection.
93      * See setExpandedNodesAndSelection/getExpandedNodesAndSelection
94      */

95     private static final class ExpandedNodesAndSelection {
96         public static final long serialVersionUID = 1L;
97         
98         /** selection */
99         public TreePath JavaDoc[] selection;
100         
101         /** expanded nodes */
102         public TreePath JavaDoc[] expandedNodes;
103     }
104     
105     private static final long serialVersionUID = 1;
106     
107     /** A subclass of JTree. */
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); // NOI18N
116

117         // Create the tree. It will be used as a renderer and editor.
118
// First we create a dummy model for the tree and set later the
119
// real model with setModel(). This way JTree's TreeModelListener
120
// will be called first and we can update our table.
121
tree = new TreeTableCellRenderer(
122             new DefaultTreeModel JavaDoc(new DefaultMutableTreeNode JavaDoc()));
123
124     // Install a tableModel representing the visible rows in the tree.
125
setTreeTableModel(treeTableModel);
126
127     // Force the JTable and JTree to share their row selection models.
128
ListToTreeSelectionModelWrapper selectionWrapper =
129             new ListToTreeSelectionModelWrapper();
130     tree.setSelectionModel(selectionWrapper);
131     setSelectionModel(selectionWrapper.getListSelectionModel());
132
133     // Install the tree editor renderer and editor.
134
setDefaultRenderer(TreeTableModel.class, tree);
135     setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
136
137     // No grid.
138
setShowGrid(false);
139
140     // No intercell spacing
141
//setIntercellSpacing(new Dimension(0, 0));
142

143     // And update the height of the trees row to match that of
144
// the table.
145
if (tree.getRowHeight() < 1) {
146         // Metal looks better like this.
147
setRowHeight(18);
148     }
149         
150         this.sortingModel = new SortingModel();
151         
152         InputMap JavaDoc imp2 = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
153          
154         // copied from TreeView which tried to fix #18292
155
// by doing this
156
imp2.put(KeyStroke.getKeyStroke("control C"), "none"); // NOI18N
157
imp2.put(KeyStroke.getKeyStroke("control V"), "none"); // NOI18N
158
imp2.put(KeyStroke.getKeyStroke("control X"), "none"); // NOI18N
159
imp2.put(KeyStroke.getKeyStroke("COPY"), "none"); // NOI18N
160
imp2.put(KeyStroke.getKeyStroke("PASTE"), "none"); // NOI18N
161
imp2.put(KeyStroke.getKeyStroke("CUT"), "none"); // NOI18N
162
imp2.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
163                 "expand"); // NOI18N
164
imp2.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
165                 "collapse"); // NOI18N
166

167         getActionMap().put("expand", // NOI18N
168
new ExpandCollapseAction(true, this));
169         getActionMap().put("collapse", // NOI18N
170
new ExpandCollapseAction(false, this));
171                 
172         // copied from TTV
173
getSortingModel().addChangeListener(new ChangeListener JavaDoc() {
174             public void stateChanged(ChangeEvent JavaDoc e) {
175                 Object JavaDoc es = getExpandedNodesAndSelection();
176
177                 getTreeTableModel().sort(getSortingModel());
178                 
179                 setExpandedNodesAndSelection(es);
180             }
181         });
182     }
183
184     /**
185      * Returns selected path
186      *
187      * @return selected path or null
188      */

189     public TreePath JavaDoc getSelectedPath() {
190         int row = getSelectedRow();
191         if (row < 0)
192             return null;
193         
194         return tree.getPathForRow(row);
195     }
196
197     /**
198      * Tests whether the column contains the tree.
199      *
200      * @param column column index in the view
201      * @return true = it is the column with the tree
202      */

203     public boolean isTreeColumn(int column) {
204         return getColumnClass(column) == TreeTableModel.class;
205     }
206     
207     /**
208      * Returns selected paths
209      *
210      * @return selected paths
211      */

212     public TreePath JavaDoc[] getSelectedPaths() {
213         int[] rows = getSelectedRows();
214         TreePath JavaDoc[] paths = new TreePath JavaDoc[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     /**
222      * TreeModel does not support a reordering event. Therefore it is
223      * necessary to save the expanded nodes and selection before
224      * a reordering and restore them after such an operation.
225      *
226      * @return an object that could be used in setExpandedNodesAndSelection()
227      */

228     public Object JavaDoc getExpandedNodesAndSelection() {
229         TreeTable.ExpandedNodesAndSelection ret =
230             new TreeTable.ExpandedNodesAndSelection();
231         
232         Enumeration JavaDoc<TreePath JavaDoc> en = tree.getExpandedDescendants(
233             new TreePath JavaDoc(getTreeTableModel().getRoot()));
234         if (en != null) {
235             List JavaDoc<TreePath JavaDoc> exp = new ArrayList JavaDoc<TreePath JavaDoc>();
236             while (en.hasMoreElements()) {
237                 exp.add(en.nextElement());
238             }
239             ret.expandedNodes = exp.toArray(new TreePath JavaDoc[exp.size()]);
240         } else {
241             ret.expandedNodes = new TreePath JavaDoc[0];
242         }
243         
244         int[] selRows = getSelectedRows();
245         ret.selection = new TreePath JavaDoc[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     /**
254      * TreeModel does not support a reordering event. Therefore it is
255      * necessary to save the expanded nodes and selection before
256      * a reordering and restore them after such an operation.
257      *
258      * @param an object that was returned by getExpandedNodesAndSelection()
259      */

260     public void setExpandedNodesAndSelection(Object JavaDoc obj) {
261         TreeTable.ExpandedNodesAndSelection es =
262             (TreeTable.ExpandedNodesAndSelection) obj;
263         
264         // expanded nodes
265
for (int i = 0; i < es.expandedNodes.length; i++) {
266             tree.expandPath(es.expandedNodes[i]);
267         }
268
269         // selection
270
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     /**
277      * Expands all nodes
278      */

279     public void expandAll() {
280         TreePath JavaDoc tp = new TreePath JavaDoc(tree.getModel().getRoot());
281         expandAllUnder(tp);
282     }
283     
284     /**
285      * Collapses all nodes
286      */

287     public void collapseAll() {
288         Object JavaDoc root = tree.getModel().getRoot();
289         TreePath JavaDoc rootPath = new TreePath JavaDoc(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     /**
302      * Expands the whole path so the last element becomes visible
303      *
304      * @param tp path
305      */

306     public void expandAllPath(TreePath JavaDoc tp) {
307         while (tp != null) {
308             tree.expandPath(tp);
309             tp = tp.getParentPath();
310         }
311     }
312
313     /**
314      * Does tree.expandPath(tp)
315      *
316      * @param tp path to be expanded
317      */

318     public void expandPath(TreePath JavaDoc tp) {
319         tree.expandPath(tp);
320     }
321     
322     /**
323      * Selects the specified path
324      *
325      * @param path the path to be selected
326      */

327     public void select(TreePath JavaDoc path) {
328         int row = this.getRowForPath(path);
329         if (row >= 0)
330             this.getSelectionModel().setSelectionInterval(row, row);
331     }
332
333     /**
334      * Selects the specified path
335      *
336      * @param path the path to be selected
337      */

338     public void select(TreePath JavaDoc[] 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     /**
348      * Makes the specified task visible (scrolls to it)
349      *
350      * @param path to make visible
351      */

352     public void scrollTo(TreePath JavaDoc path) {
353         int row = this.getRowForPath(path);
354         if (row > 0) {
355             Rectangle JavaDoc r = this.getCellRect(row, 0, true);
356             this.scrollRectToVisible(r);
357         }
358     }
359     
360     /**
361      * Expands all nodes under the specified path
362      *
363      * @param tp the path
364      */

365     public void expandAllUnder(TreePath JavaDoc tp) {
366         tree.expandPath(tp);
367         Object JavaDoc last = tp.getLastPathComponent();
368         for (int i = 0; i < tree.getModel().getChildCount(last); i++) {
369             Object JavaDoc child = tree.getModel().getChild(last, i);
370             expandAllUnder(tp.pathByAddingChild(child));
371         }
372     }
373     
374     /**
375      * Collapses all nodes under the specified path
376      *
377      * @param tp the path
378      */

379     public void collapseAllUnder(TreePath JavaDoc tp) {
380         if (!tree.hasBeenExpanded(tp))
381             return;
382         
383         Object JavaDoc last = tp.getLastPathComponent();
384         for (int i = 0; i < tree.getModel().getChildCount(last); i++) {
385             Object JavaDoc child = tree.getModel().getChild(last, i);
386             collapseAllUnder(tp.pathByAddingChild(child));
387         }
388         tree.collapsePath(tp);
389     }
390     
391     /**
392      * Sets new sorting model
393      *
394      * @param sm new sorting model or null
395      */

396     public void setSortingModel(SortingModel sm) {
397         SortingModel old = this.sortingModel;
398         this.sortingModel = sm;
399         firePropertyChange("sortingModel", old, sm); // NOI18N
400
}
401     
402     /**
403      * Returns sorting model
404      *
405      * @return sorting model or null if not supported
406      */

407     public SortingModel getSortingModel() {
408         return sortingModel;
409     }
410     
411     /**
412      * Sets new TreeTableModel
413      *
414      * @param treeTableModel a model
415      */

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     /**
425      * Returns the current model
426      *
427      * @return model
428      */

429     public TreeTableModel getTreeTableModel() {
430         return treeTableModel;
431     }
432     
433     /**
434      * Returns the object for the specified row
435      *
436      * @param row row number
437      */

438     public Object JavaDoc getNodeForRow(int row) {
439         TreePath JavaDoc tp = tree.getPathForRow(row);
440         // debug UTUtils.LOGGER.fine(row + " -> " + tp);
441
return tp.getLastPathComponent();
442     }
443     
444     /**
445      * Returns the row corresponding to the specified path
446      *
447      * @param path path to a node
448      * @return corresponding row in the table
449      */

450     public int getRowForPath(TreePath JavaDoc path) {
451         return tree.getRowForPath(path);
452     }
453     
454     /**
455      * Overridden to message super and forward the method to the tree.
456      * Since the tree is not actually in the component hieachy it will
457      * never receive this unless we forward it in this manner.
458      */

459     public void updateUI() {
460     super.updateUI();
461     if(tree != null) {
462         tree.updateUI();
463         // Do this so that the editor is referencing the current renderer
464
// from the tree. The renderer can potentially change each time
465
// laf changes.
466
setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
467     }
468     // Use the tree's default foreground and background colors in the
469
// table.
470
LookAndFeel.installColorsAndFont(this, "Tree.background", // NOI18N
471
"Tree.foreground", "Tree.font"); // NOI18N
472
}
473
474     /**
475      * Returns the actual row that is editing as <code>getEditingRow</code>
476      * will always return -1.
477      */

478     private int realEditingRow() {
479     return editingRow;
480     }
481
482     /**
483      * This is overridden to invoke super's implementation, and then,
484      * if the receiver is editing a Tree column, the editor's bounds is
485      * reset. The reason we have to do this is because JTable doesn't
486      * think the table is being edited, as <code>getEditingRow</code> returns
487      * -1, and therefore doesn't automatically resize the editor for us.
488      */

489     public void sizeColumnsToFit(int resizingColumn) {
490     super.sizeColumnsToFit(resizingColumn);
491     if (getEditingColumn() != -1 && getColumnClass(editingColumn) ==
492         TreeTableModel.class) {
493         Rectangle JavaDoc cellRect = getCellRect(realEditingRow(),
494                          getEditingColumn(), false);
495             Component JavaDoc component = getEditorComponent();
496         component.setBounds(cellRect);
497             component.validate();
498     }
499     }
500
501     /**
502      * Overridden to invoke repaint for the particular location if
503      * the column contains the tree. This is done as the tree editor does
504      * not fill the bounds of the cell, we need the renderer to paint
505      * the tree in the background, and then draw the editor over it.
506      */

507     public boolean editCellAt(int row, int column, EventObject JavaDoc 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 JavaDoc 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     /**
532     /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
533      * paint the editor. The UI currently uses different techniques to
534      * paint the renderers and editors and overriding setBounds() below
535      * is not the right thing to do for an editor. Returning -1 for the
536      * editing row in this case, ensures the editor is never painted.
537      */

538     public int getEditingRow() {
539         return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 :
540             editingRow;
541     }
542
543     /**
544      * Overridden to pass the new rowHeight to the tree.
545      */

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     /**
554      * Returns the tree that is being shared between the model.
555      */

556     public JTree JavaDoc getTree() {
557     return tree;
558     }
559
560     protected javax.swing.table.JTableHeader JavaDoc createDefaultTableHeader() {
561         return new SortableTableHeader(columnModel);
562     }
563
564     /**
565      * Serialization of a node. Instead of serializing nodes some sort of
566      * handles to this nodes will be serialized. Override this method
567      * to return some handle object. Default implementation just return
568      * <code>node</code>
569      *
570      * @param node a node from this tree
571      * @return handle for the node. != null
572      */

573     protected Serializable JavaDoc writeReplaceNode(Object JavaDoc node) {
574         return (Serializable JavaDoc) node;
575     }
576     
577     /**
578      * Deserialization of a node. Override this method to resolve
579      * node object during deserialization.
580      *
581      * @param parent parent node from this TT
582      * @param node object read from the stream
583      * @return a node from this tree or null
584      */

585     protected Object JavaDoc readResolveNode(Object JavaDoc parent, Object JavaDoc node) {
586         return node;
587     }
588
589     /**
590      * Returns the columns configuration that could be serialized.
591      *
592      * @return columns configuration (visible columns, sorting etc.)
593      */

594     public ColumnsConfig getColumnsConfig() {
595         ColumnsConfig cc = new ColumnsConfig();
596         
597         TableColumnModel JavaDoc ctm = getColumnModel();
598         assert ctm != null : "ctm == null"; // NOI18N
599

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 JavaDoc 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     /**
615      * Sets columns configuration read from a stream.
616      *
617      * @param config columns configuration
618      */

619     public void setColumnsConfig(ColumnsConfig config) {
620         assert config != null : "config == null"; // NOI18N
621

622         this.createDefaultColumnsFromModel();
623
624         ColumnsConfig cc = (ColumnsConfig) config;
625         
626         ArrayList JavaDoc<TableColumn JavaDoc> newc = new ArrayList JavaDoc<TableColumn JavaDoc>();
627         TableColumnModel JavaDoc tcm = getColumnModel();
628         assert tcm != null : "tcm == null"; // NOI18N
629

630         for (int i = 0; i < cc.columns.length; i++) {
631             for (int j = 0; j < tcm.getColumnCount(); j++) {
632                 TableColumn JavaDoc 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     /**
651      * Returns all expanded nodes
652      *
653      * @return expanded nodes
654      */

655     public TreePath JavaDoc[] getExpandedNodes() {
656         Enumeration JavaDoc<TreePath JavaDoc> en = tree.getExpandedDescendants(
657             new TreePath JavaDoc(getTreeTableModel().getRoot()));
658         
659         List JavaDoc<TreePath JavaDoc> paths = new ArrayList JavaDoc<TreePath JavaDoc>();
660         if (en != null) {
661             while (en.hasMoreElements()) {
662                 paths.add(en.nextElement());
663             }
664         }
665         
666         return paths.toArray(new TreePath JavaDoc[paths.size()]);
667     }
668     
669     /**
670      * Sets expanded nodes
671      *
672      * @param n expanded nodes
673      */

674     public void setExpandedNodes(TreePath JavaDoc[] n) {
675         for (int i = 0; i < n.length; i++) {
676             TreePath JavaDoc tp = n[i];
677             expandPath(tp);
678         }
679     }
680     
681     /**
682      * Replaces expanded nodes from this TT for writing into a stream.
683      *
684      * @param n nodes to be written
685      * @return replacement
686      */

687     public Serializable JavaDoc writeReplaceExpandedNodes(TreePath JavaDoc[] n) {
688         List JavaDoc<Serializable JavaDoc> paths = new ArrayList JavaDoc<Serializable JavaDoc>();
689         for (int i = 0; i < n.length; i++) {
690             paths.add(writeReplaceTreePath((TreePath JavaDoc) n[i]));
691         }
692         
693         return (Serializable JavaDoc) paths;
694     }
695     
696     /**
697      * Resolves expanded nodes in this TT from an object read from a stream.
698      *
699      * @return ser expanded nodes or null
700      * @param o read object
701      */

702     public TreePath JavaDoc[] readResolveExpandedNodes(Object JavaDoc ser) {
703         if (ser == null)
704             return new TreePath JavaDoc[0];
705         
706         List JavaDoc<TreePath JavaDoc> ret = new ArrayList JavaDoc<TreePath JavaDoc>();
707         List JavaDoc l = (List JavaDoc) ser;
708         for (int i = 0; i < l.size(); i++) {
709             TreePath JavaDoc tp = readResolveTreePath(l.get(i));
710             if (tp != null)
711                 ret.add(tp);
712         }
713         
714         return ret.toArray(new TreePath JavaDoc[ret.size()]);
715     }
716
717     /**
718      * Replaces a tree path from this TT for writing into a stream.
719      *
720      * @param tp a path from this TT
721      * @return replacement
722      */

723     public Serializable JavaDoc writeReplaceTreePath(TreePath JavaDoc tp) {
724         Object JavaDoc[] 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     /**
733      * Resolves a path in this TT from an object read from a stream.
734      *
735      * @return tp a path from this TT or null
736      * @param o read object
737      */

738     public TreePath JavaDoc readResolveTreePath(Object JavaDoc o) {
739         Object JavaDoc[] p = (Object JavaDoc[]) 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 JavaDoc(p);
747     }
748
749     /**
750      * Returns all expanded nodes under the specified one.
751      *
752      * @param path a path
753      * @return all expanded paths (possibly one with <code>n</code> as the
754      * last component)
755      */

756     public TreePath JavaDoc[] getExpandedNodesUnder(TreePath JavaDoc path) {
757         Enumeration JavaDoc<TreePath JavaDoc> en = tree.getExpandedDescendants(path);
758         
759         List JavaDoc<TreePath JavaDoc> paths = new ArrayList JavaDoc<TreePath JavaDoc>();
760         if (en != null) {
761             while (en.hasMoreElements()) {
762                 paths.add(en.nextElement());
763             }
764         }
765         
766         return paths.toArray(new TreePath JavaDoc[paths.size()]);
767     }
768
769     /*
770      * For profiling only.
771      *
772     public void paint(Graphics g) {
773         super.paint(g);
774     }*/

775     
776     /**
777      * A TreeCellRenderer that displays a JTree.
778      */

779     public class TreeTableCellRenderer extends JTree JavaDoc implements
780              TableCellRenderer JavaDoc {
781
782         private static final long serialVersionUID = 1;
783
784     /** Last table/tree row asked to renderer. */
785     protected int visibleRow;
786         private Border JavaDoc border;
787
788     public TreeTableCellRenderer(TreeModel JavaDoc model) {
789         super(model);
790     }
791
792     /**
793      * updateUI is overridden to set the colors of the Tree's renderer
794      * to match that of the table.
795      */

796     public void updateUI() {
797         super.updateUI();
798         // Make the tree's cell renderer use the table's cell selection
799
// colors.
800
TreeCellRenderer JavaDoc tcr = getCellRenderer();
801         if (tcr instanceof DefaultTreeCellRenderer JavaDoc) {
802         DefaultTreeCellRenderer JavaDoc dtcr = ((DefaultTreeCellRenderer JavaDoc)tcr);
803         // For 1.1 uncomment this, 1.2 has a bug that will cause an
804
// exception to be thrown if the border selection color is
805
// null.
806
// dtcr.setBorderSelectionColor(null);
807
dtcr.setTextSelectionColor(UIManager.getColor
808                        ("Table.selectionForeground")); // NOI18N
809
dtcr.setBackgroundSelectionColor(UIManager.getColor
810                         ("Table.selectionBackground")); // NOI18N
811
}
812     }
813
814     /**
815      * Sets the row height of the tree, and forwards the row height to
816      * the table.
817      */

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     /**
829      * This is overridden to set the height to match that of the JTable.
830      */

831     public void setBounds(int x, int y, int w, int h) {
832         super.setBounds(x, 0, w, TreeTable.this.getHeight());
833     }
834
835     /**
836      * Sublcassed to translate the graphics such that the last visible
837      * row will be drawn at 0,0.
838      */

839     public void paint(Graphics JavaDoc g) {
840             /*Graphics2D g2 = (Graphics2D) g;
841             g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
842                 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
843             g2.setRenderingHint(RenderingHints.KEY_RENDERING,
844                 RenderingHints.VALUE_RENDER_QUALITY);*/

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     /**
855      * TreeCellRenderer method. Overridden to update the visible row.
856      * Original code
857     public Component getTableCellRendererComponent(JTable table,
858                                Object value,
859                                boolean isSelected,
860                                boolean hasFocus,
861                                int row, int column) {
862         Color background;
863         Color foreground;
864
865         if(isSelected) {
866         background = table.getSelectionBackground();
867         foreground = table.getSelectionForeground();
868         }
869         else {
870         background = table.getBackground();
871         foreground = table.getForeground();
872         }
873         highlightBorder = null;
874         if (realEditingRow() == row && getEditingColumn() == column) {
875         background = UIManager.getColor("Table.focusCellBackground");
876         foreground = UIManager.getColor("Table.focusCellForeground");
877         }
878         else if (hasFocus) {
879         highlightBorder = UIManager.getBorder
880                           ("Table.focusCellHighlightBorder");
881         if (isCellEditable(row, column)) {
882             background = UIManager.getColor
883                      ("Table.focusCellBackground");
884             foreground = UIManager.getColor
885                      ("Table.focusCellForeground");
886         }
887         }
888
889         visibleRow = row;
890         setBackground(background);
891         
892         TreeCellRenderer tcr = getCellRenderer();
893         if (tcr instanceof DefaultTreeCellRenderer) {
894         DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr);
895         if (isSelected) {
896             dtcr.setTextSelectionColor(foreground);
897             dtcr.setBackgroundSelectionColor(background);
898         }
899         else {
900             dtcr.setTextNonSelectionColor(foreground);
901             dtcr.setBackgroundNonSelectionColor(background);
902         }
903         }
904         return this;
905     }
906     }*/

907     /**
908      * TreeCellRenderer method. Overridden to update the visible row.
909      */

910     public Component JavaDoc getTableCellRendererComponent(JTable JavaDoc table,
911                                Object JavaDoc value,
912                                boolean isSelected,
913                                boolean hasFocus,
914                                int row, int column) {
915             if (hasFocus) {
916                 border = UIManager.getBorder("Table.focusCellHighlightBorder"); // NOI18N
917
if (table.isCellEditable(row, column)) {
918                     super.setForeground( UIManager.getColor("Table.focusCellForeground") ); // NOI18N
919
super.setBackground( UIManager.getColor("Table.focusCellBackground") ); // NOI18N
920
}
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     /**
937      * An editor that can be used to edit the tree column. This extends
938      * DefaultCellEditor and uses a JTextField (actually, TreeTableTextField)
939      * to perform the actual editing.
940      * <p>To support editing of the tree column we can not make the tree
941      * editable. The reason this doesn't work is that you can not use
942      * the same component for editing and renderering. The table may have
943      * the need to paint cells, while a cell is being edited. If the same
944      * component were used for the rendering and editing the component would
945      * be moved around, and the contents would change. When editing, this
946      * is undesirable, the contents of the text field must stay the same,
947      * including the caret blinking, and selections persisting. For this
948      * reason the editing is done via a TableCellEditor.
949      * <p>Another interesting thing to be aware of is how tree positions
950      * its render and editor. The render/editor is responsible for drawing the
951      * icon indicating the type of node (leaf, branch...). The tree is
952      * responsible for drawing any other indicators, perhaps an additional
953      * +/- sign, or lines connecting the various nodes. So, the renderer
954      * is positioned based on depth. On the other hand, table always makes
955      * its editor fill the contents of the cell. To get the allusion
956      * that the table cell editor is part of the tree, we don't want the
957      * table cell editor to fill the cell bounds. We want it to be placed
958      * in the same manner as tree places it editor, and have table message
959      * the tree to paint any decorations the tree wants. Then, we would
960      * only have to worry about the editing part. The approach taken
961      * here is to determine where tree would place the editor, and to override
962      * the <code>reshape</code> method in the JTextField component to
963      * nudge the textfield to the location tree would place it. Since
964      * JTreeTable will paint the tree behind the editor everything should
965      * just work. So, that is what we are doing here. Determining of
966      * the icon position will only work if the TreeCellRenderer is
967      * an instance of DefaultTreeCellRenderer. If you need custom
968      * TreeCellRenderers, that don't descend from DefaultTreeCellRenderer,
969      * and you want to support editing in JTreeTable, you will have
970      * to do something similiar.
971      */

972     public class TreeTableCellEditor extends DefaultCellEditor JavaDoc {
973     public TreeTableCellEditor() {
974         super(new TreeTableTextField());
975     }
976
977     /**
978      * Overridden to determine an offset that tree would place the
979      * editor at. The offset is determined from the
980      * <code>getRowBounds</code> JTree method, and additionally
981      * from the icon DefaultTreeCellRenderer will use.
982      * <p>The offset is then set on the TreeTableTextField component
983      * created in the constructor, and returned.
984      */

985     public Component JavaDoc getTableCellEditorComponent(JTable JavaDoc table,
986                              Object JavaDoc value,
987                              boolean isSelected,
988                              int r, int c) {
989         Component JavaDoc component = super.getTableCellEditorComponent
990         (table, value, isSelected, r, c);
991         JTree JavaDoc t = getTree();
992         Rectangle JavaDoc bounds = t.getRowBounds(r);
993         int offset = bounds.x;
994             Rectangle JavaDoc 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    /**
1002     * This is overridden to forward the event to the tree. This will
1003     * return true if the click count >= 3, or the event is null.
1004     */

1005    public boolean isCellEditable(EventObject JavaDoc e) {
1006            /* DEBUG if (UTUtils.LOGGER.isLoggable(Level.FINE))
1007                Thread.dumpStack();
1008            UTUtils.LOGGER.fine(e.toString());*/

1009            if (e instanceof MouseEvent JavaDoc) {
1010        MouseEvent JavaDoc me = (MouseEvent JavaDoc)e;
1011                
1012        // If the modifiers are not 0 (or the left mouse button),
1013
// tree may try and toggle the selection, and table
1014
// will then try and toggle, resulting in the
1015
// selection remaining the same. To avoid this, we
1016
// only dispatch when the modifiers are 0 (or the left mouse
1017
// button).
1018
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 JavaDoc newME = new MouseEvent JavaDoc
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                /*
1035                int row = TreeTable.this.rowAtPoint(me.getPoint());
1036                int col = TreeTable.this.columnAtPoint(me.getPoint());
1037                int selCol = TreeTable.this.getSelectedColumn();
1038                int selRow = TreeTable.this.getSelectedRow();
1039                if (row == selRow && col == selCol)
1040                    return true;
1041                 */

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    /**
1055     * Component used by TreeTableCellEditor. The only thing this does
1056     * is to override the <code>reshape</code> method, and to ALWAYS
1057     * make the x location be <code>offset</code>.
1058     */

1059    static class TreeTableTextField extends JTextField JavaDoc {
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    /**
1070     * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
1071     * to listen for changes in the ListSelectionModel it maintains. Once
1072     * a change in the ListSelectionModel happens, the paths are updated
1073     * in the DefaultTreeSelectionModel.
1074     */

1075    class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel JavaDoc {
1076
1077        private static final long serialVersionUID = 1;
1078
1079    /** Set to true when we are updating the ListSelectionModel. */
1080    protected boolean updatingListSelectionModel;
1081
1082    public ListToTreeSelectionModelWrapper() {
1083        super();
1084        getListSelectionModel().addListSelectionListener
1085                                (createListSelectionListener());
1086    }
1087
1088    /**
1089     * Returns the list selection model. ListToTreeSelectionModelWrapper
1090     * listens for changes to this model and updates the selected paths
1091     * accordingly.
1092     */

1093    ListSelectionModel JavaDoc getListSelectionModel() {
1094        return listSelectionModel;
1095    }
1096
1097    /**
1098     * This is overridden to set <code>updatingListSelectionModel</code>
1099     * and message super. This is the only place DefaultTreeSelectionModel
1100     * alters the ListSelectionModel.
1101     */

1102    public void resetRowSelection() {
1103        if(!updatingListSelectionModel) {
1104        updatingListSelectionModel = true;
1105        try {
1106            super.resetRowSelection();
1107        }
1108        finally {
1109            updatingListSelectionModel = false;
1110        }
1111        }
1112        // Notice how we don't message super if
1113
// updatingListSelectionModel is true. If
1114
// updatingListSelectionModel is true, it implies the
1115
// ListSelectionModel has already been updated and the
1116
// paths are the only thing that needs to be updated.
1117
}
1118
1119    /**
1120     * Creates and returns an instance of ListSelectionHandler.
1121     */

1122    protected ListSelectionListener JavaDoc createListSelectionListener() {
1123        return new ListSelectionHandler();
1124    }
1125
1126    /**
1127     * If <code>updatingListSelectionModel</code> is false, this will
1128     * reset the selected paths from the selected rows in the list
1129     * selection model.
1130     */

1131    protected void updateSelectedPathsFromSelectedRows() {
1132        if(!updatingListSelectionModel) {
1133        updatingListSelectionModel = true;
1134        try {
1135            // This is way expensive, ListSelectionModel needs an
1136
// enumerator for iterating.
1137
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 JavaDoc 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    /**
1161     * Class responsible for calling updateSelectedPathsFromSelectedRows
1162     * when the selection of the list changse.
1163     */

1164    class ListSelectionHandler implements ListSelectionListener JavaDoc {
1165        public void valueChanged(ListSelectionEvent JavaDoc e) {
1166                UTUtils.LOGGER.fine("changed"); // NOI18N
1167
updateSelectedPathsFromSelectedRows();
1168        }
1169    }
1170    }
1171}
1172
Popular Tags