Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/puppycrawl/tools/checkstyle/gui/JTreeTable.java


1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2002  Oliver Burn
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  /*
21   * @(#)JTreeTable.java  1.2 98/10/27
22   *
23   * Copyright 1997, 1998 by Sun Microsystems, Inc.,
24   * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
25   * All rights reserved.
26   *
27   * This software is the confidential and proprietary information
28   * of Sun Microsystems, Inc. ("Confidential Information").  You
29   * shall not disclose such Confidential Information and shall use
30   * it only in accordance with the terms of the license agreement
31   * you entered into with Sun.
32   */
33  
34  package com.puppycrawl.tools.checkstyle.gui;
35  
36  import java.awt.Component;
37  import java.awt.Dimension;
38  import java.awt.Graphics;
39  import java.awt.event.ActionEvent;
40  import java.awt.event.MouseEvent;
41  import java.util.EventObject;
42  import javax.swing.Action;
43  import javax.swing.AbstractAction;
44  import javax.swing.JTable;
45  import javax.swing.JTree;
46  import javax.swing.KeyStroke;
47  import javax.swing.ListSelectionModel;
48  import javax.swing.LookAndFeel;
49  import javax.swing.UIManager;
50  import javax.swing.event.ListSelectionEvent;
51  import javax.swing.event.ListSelectionListener;
52  import javax.swing.table.TableCellEditor;
53  import javax.swing.table.TableCellRenderer;
54  import javax.swing.tree.DefaultTreeCellRenderer;
55  import javax.swing.tree.DefaultTreeSelectionModel;
56  import javax.swing.tree.TreeCellRenderer;
57  import javax.swing.tree.TreeModel;
58  import javax.swing.tree.TreePath;
59  
60  /**
61   * This example shows how to create a simple JTreeTable component,
62   * by using a JTree as a renderer (and editor) for the cells in a
63   * particular column in the JTable.
64   *
65   * @version 1.2 10/27/98
66   *
67   * @author Philip Milne
68   * @author Scott Violet
69   * @author Lars Kühne
70   */
71  public class JTreeTable extends JTable
72  {
73      /** A subclass of JTree. */
74      protected TreeTableCellRenderer tree;
75  
76      public JTreeTable(TreeTableModel treeTableModel)
77      {
78          super();
79  
80          // Create the tree. It will be used as a renderer and editor.
81          tree = new TreeTableCellRenderer(treeTableModel);
82  
83          // Install a tableModel representing the visible rows in the tree.
84          super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
85  
86          // Force the JTable and JTree to share their row selection models.
87          ListToTreeSelectionModelWrapper selectionWrapper = new
88                  ListToTreeSelectionModelWrapper();
89          tree.setSelectionModel(selectionWrapper);
90          setSelectionModel(selectionWrapper.getListSelectionModel());
91  
92          // Install the tree editor renderer and editor.
93          setDefaultRenderer(TreeTableModel.class, tree);
94          setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
95  
96          // No grid.
97          setShowGrid(false);
98  
99          // No intercell spacing
100         setIntercellSpacing(new Dimension(0, 0));
101 
102         // And update the height of the trees row to match that of
103         // the table.
104         if (tree.getRowHeight() < 1) {
105             // Metal looks better like this.
106             setRowHeight(getRowHeight());
107         }
108 
109         Action expand = new AbstractAction() {
110                 public void actionPerformed(ActionEvent e) {
111                     TreePath selected = tree.getSelectionPath();
112                     if (tree.isExpanded(selected)) {
113                         tree.collapsePath(selected);
114                     }
115                     else {
116                         tree.expandPath(selected);
117                     }
118                     tree.setSelectionPath(selected);
119                 }
120             };
121         KeyStroke stroke = KeyStroke.getKeyStroke("ENTER");
122         String command = "expand/collapse";
123         getInputMap().put(stroke, command);
124         getActionMap().put(command, expand);
125     }
126 
127     /**
128      * Overridden to message super and forward the method to the tree.
129      * Since the tree is not actually in the component hierarchy it will
130      * never receive this unless we forward it in this manner.
131      */
132     public void updateUI()
133     {
134         super.updateUI();
135         if (tree != null) {
136             tree.updateUI();
137         }
138         // Use the tree's default foreground and background colors in the
139         // table.
140         LookAndFeel.installColorsAndFont(this, "Tree.background",
141                 "Tree.foreground", "Tree.font");
142     }
143 
144     /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
145      * paint the editor. The UI currently uses different techniques to
146      * paint the renderers and editors and overriding setBounds() below
147      * is not the right thing to do for an editor. Returning -1 for the
148      * editing row in this case, ensures the editor is never painted.
149      */
150     public int getEditingRow()
151     {
152         final Class editingClass = getColumnClass(editingColumn);
153         return (editingClass == TreeTableModel.class) ? -1 : editingRow;
154     }
155 
156     /**
157      * Overridden to pass the new rowHeight to the tree.
158      */
159     public void setRowHeight(int rowHeight)
160     {
161         super.setRowHeight(rowHeight);
162         if (tree != null && tree.getRowHeight() != rowHeight) {
163             tree.setRowHeight(getRowHeight());
164         }
165     }
166 
167     /**
168      * @return the tree that is being shared between the model.
169      */
170     public JTree getTree()
171     {
172         return tree;
173     }
174 
175     /**
176      * A TreeCellRenderer that displays a JTree.
177      */
178     class TreeTableCellRenderer extends JTree implements
179             TableCellRenderer
180     {
181         /** Last table/tree row asked to renderer. */
182         protected int visibleRow;
183 
184         /** creates a new instance */
185         public TreeTableCellRenderer(TreeModel model)
186         {
187             super(model);
188         }
189 
190         /**
191          * updateUI is overridden to set the colors of the Tree's renderer
192          * to match that of the table.
193          */
194         public void updateUI()
195         {
196             super.updateUI();
197             // Make the tree's cell renderer use the table's cell selection
198             // colors.
199             TreeCellRenderer tcr = getCellRenderer();
200             if (tcr instanceof DefaultTreeCellRenderer) {
201                 DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr);
202                 // For 1.1 uncomment this, 1.2 has a bug that will cause an
203                 // exception to be thrown if the border selection color is
204                 // null.
205                 // dtcr.setBorderSelectionColor(null);
206                 dtcr.setTextSelectionColor(UIManager.getColor
207                         ("Table.selectionForeground"));
208                 dtcr.setBackgroundSelectionColor(UIManager.getColor
209                         ("Table.selectionBackground"));
210             }
211         }
212 
213         /**
214          * Sets the row height of the tree, and forwards the row height to
215          * the table.
216          */
217         public void setRowHeight(int rowHeight)
218         {
219             if (rowHeight > 0) {
220                 super.setRowHeight(rowHeight);
221                 if (JTreeTable.this != null &&
222                         JTreeTable.this.getRowHeight() != rowHeight) {
223                     JTreeTable.this.setRowHeight(getRowHeight());
224                 }
225             }
226         }
227 
228         /**
229          * This is overridden to set the height to match that of the JTable.
230          */
231         public void setBounds(int x, int y, int w, int h)
232         {
233             super.setBounds(x, 0, w, JTreeTable.this.getHeight());
234         }
235 
236         /**
237          * Sublcassed to translate the graphics such that the last visible
238          * row will be drawn at 0,0.
239          */
240         public void paint(Graphics g)
241         {
242             g.translate(0, -visibleRow * getRowHeight());
243             super.paint(g);
244         }
245 
246         /**
247          * TreeCellRenderer method. Overridden to update the visible row.
248          * @see TableCellRenderer
249          */
250         public Component getTableCellRendererComponent(JTable table,
251                 Object value,
252                 boolean isSelected,
253                 boolean hasFocus,
254                 int row, int column)
255         {
256             if (isSelected) {
257                 setBackground(table.getSelectionBackground());
258             } else {
259                 setBackground(table.getBackground());
260             }
261 
262             visibleRow = row;
263             return this;
264         }
265     }
266 
267 
268     /**
269      * TreeTableCellEditor implementation. Component returned is the
270      * JTree.
271      */
272     public class TreeTableCellEditor extends AbstractCellEditor implements
273             TableCellEditor
274     {
275         public Component getTableCellEditorComponent(JTable table,
276                 Object value,
277                 boolean isSelected,
278                 int r, int c)
279         {
280             return tree;
281         }
282 
283         /**
284          * Overridden to return false, and if the event is a mouse event
285          * it is forwarded to the tree.<p>
286          * The behavior for this is debatable, and should really be offered
287          * as a property. By returning false, all keyboard actions are
288          * implemented in terms of the table. By returning true, the
289          * tree would get a chance to do something with the keyboard
290          * events. For the most part this is ok. But for certain keys,
291          * such as left/right, the tree will expand/collapse where as
292          * the table focus should really move to a different column. Page
293          * up/down should also be implemented in terms of the table.
294          * By returning false this also has the added benefit that clicking
295          * outside of the bounds of the tree node, but still in the tree
296          * column will select the row, whereas if this returned true
297          * that wouldn't be the case.
298          * <p>By returning false we are also enforcing the policy that
299          * the tree will never be editable (at least by a key sequence).
300          *
301          * @see TableCellEditor
302          */
303         public boolean isCellEditable(EventObject e)
304         {
305             if (e instanceof MouseEvent) {
306                 for (int counter = getColumnCount() - 1; counter >= 0;
307                      counter--) {
308                     if (getColumnClass(counter) == TreeTableModel.class) {
309                         MouseEvent me = (MouseEvent) e;
310                         MouseEvent newME = new MouseEvent(tree, me.getID(),
311                                 me.getWhen(), me.getModifiers(),
312                                 me.getX() - getCellRect(0, counter, true).x,
313                                 me.getY(), me.getClickCount(),
314                                 me.isPopupTrigger());
315                         tree.dispatchEvent(newME);
316                         break;
317                     }
318                 }
319             }
320 
321             return false;
322         }
323     }
324 
325 
326     /**
327      * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
328      * to listen for changes in the ListSelectionModel it maintains. Once
329      * a change in the ListSelectionModel happens, the paths are updated
330      * in the DefaultTreeSelectionModel.
331      */
332     class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
333     {
334         /** Set to true when we are updating the ListSelectionModel. */
335         protected boolean updatingListSelectionModel;
336 
337         public ListToTreeSelectionModelWrapper()
338         {
339             super();
340             getListSelectionModel().addListSelectionListener
341                     (createListSelectionListener());
342         }
343 
344         /**
345          * Returns the list selection model. ListToTreeSelectionModelWrapper
346          * listens for changes to this model and updates the selected paths
347          * accordingly.
348          *
349          * @return the list selection model
350          */
351         ListSelectionModel getListSelectionModel()
352         {
353             return listSelectionModel;
354         }
355 
356         /**
357          * This is overridden to set <code>updatingListSelectionModel</code>
358          * and message super. This is the only place DefaultTreeSelectionModel
359          * alters the ListSelectionModel.
360          */
361         public void resetRowSelection()
362         {
363             if (!updatingListSelectionModel) {
364                 updatingListSelectionModel = true;
365                 try {
366                     super.resetRowSelection();
367                 } finally {
368                     updatingListSelectionModel = false;
369                 }
370             }
371             // Notice how we don't message super if
372             // updatingListSelectionModel is true. If
373             // updatingListSelectionModel is true, it implies the
374             // ListSelectionModel has already been updated and the
375             // paths are the only thing that needs to be updated.
376         }
377 
378         /**
379          * Creates and returns an instance of ListSelectionHandler.
380          */
381         private ListSelectionListener createListSelectionListener()
382         {
383             return new ListSelectionHandler();
384         }
385 
386         /**
387          * If <code>updatingListSelectionModel</code> is false, this will
388          * reset the selected paths from the selected rows in the list
389          * selection model.
390          */
391         protected void updateSelectedPathsFromSelectedRows()
392         {
393             if (!updatingListSelectionModel) {
394                 updatingListSelectionModel = true;
395                 try {
396                     // This is way expensive, ListSelectionModel needs an
397                     // enumerator for iterating.
398                     int min = listSelectionModel.getMinSelectionIndex();
399                     int max = listSelectionModel.getMaxSelectionIndex();
400 
401                     clearSelection();
402                     if (min != -1 && max != -1) {
403                         for (int counter = min; counter <= max; counter++) {
404                             if (listSelectionModel.isSelectedIndex(counter)) {
405                                 TreePath selPath = tree.getPathForRow
406                                         (counter);
407 
408                                 if (selPath != null) {
409                                     addSelectionPath(selPath);
410                                 }
411                             }
412                         }
413                     }
414                 } finally {
415                     updatingListSelectionModel = false;
416                 }
417             }
418         }
419 
420         /**
421          * Class responsible for calling updateSelectedPathsFromSelectedRows
422          * when the selection of the list changse.
423          */
424         class ListSelectionHandler implements ListSelectionListener
425         {
426             public void valueChanged(ListSelectionEvent e)
427             {
428                 updateSelectedPathsFromSelectedRows();
429             }
430         }
431     }
432 }