JXTreeTable.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:63k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: JXTreeTable.java,v 1.24 2005/10/12 11:26:54 kleopatra Exp $
  3.  *
  4.  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.swingx;
  22. import java.awt.Component;
  23. import java.awt.Dimension;
  24. import java.awt.Graphics;
  25. import java.awt.Point;
  26. import java.awt.Rectangle;
  27. import java.awt.event.ActionEvent;
  28. import java.awt.event.InputEvent;
  29. import java.awt.event.MouseEvent;
  30. import java.beans.PropertyChangeEvent;
  31. import java.beans.PropertyChangeListener;
  32. import java.util.Enumeration;
  33. import java.util.EventObject;
  34. import javax.swing.ActionMap;
  35. import javax.swing.Icon;
  36. import javax.swing.JTable;
  37. import javax.swing.JTree;
  38. import javax.swing.ListSelectionModel;
  39. import javax.swing.SwingUtilities;
  40. import javax.swing.UIManager;
  41. import javax.swing.border.Border;
  42. import javax.swing.event.ListSelectionEvent;
  43. import javax.swing.event.ListSelectionListener;
  44. import javax.swing.event.TreeExpansionEvent;
  45. import javax.swing.event.TreeExpansionListener;
  46. import javax.swing.event.TreeModelEvent;
  47. import javax.swing.event.TreeModelListener;
  48. import javax.swing.event.TreeSelectionListener;
  49. import javax.swing.event.TreeWillExpandListener;
  50. import javax.swing.plaf.UIResource;
  51. import javax.swing.plaf.basic.BasicTreeUI;
  52. import javax.swing.table.AbstractTableModel;
  53. import javax.swing.table.TableCellRenderer;
  54. import javax.swing.table.TableModel;
  55. import javax.swing.tree.DefaultTreeCellRenderer;
  56. import javax.swing.tree.DefaultTreeSelectionModel;
  57. import javax.swing.tree.TreeCellRenderer;
  58. import javax.swing.tree.TreePath;
  59. import javax.swing.tree.TreeSelectionModel;
  60. import org.jdesktop.swingx.JXTree.DelegatingRenderer;
  61. import org.jdesktop.swingx.decorator.ComponentAdapter;
  62. import org.jdesktop.swingx.treetable.AbstractTreeTableModel;
  63. import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
  64. import org.jdesktop.swingx.treetable.TreeTableCellEditor;
  65. import org.jdesktop.swingx.treetable.TreeTableModel;
  66. /**
  67.  * <p><code>JXTreeTable</code> is a specialized {@link javax.swing.JTable table}
  68.  * consisting of a single column in which to display hierarchical data, and any
  69.  * number of other columns in which to display regular data. The interface for
  70.  * the data model used by a <code>JXTreeTable</code> is
  71.  * {@link org.jdesktop.swingx.treetable.TreeTableModel}. It extends the
  72.  * {@link javax.swing.tree.TreeModel} interface to allow access to cell data by
  73.  * column indices within each node of the tree hierarchy.</p>
  74.  *
  75.  * <p>The most straightforward way create and use a <code>JXTreeTable</code>, is to
  76.  * first create a suitable data model for it, and pass that to a
  77.  * <code>JXTreeTable</code> constructor, as shown below:
  78.  * <pre>
  79.  *  TreeTableModel  treeTableModel = new FileSystemModel(); // any TreeTableModel
  80.  *  JXTreeTable      treeTable = new JXTreeTable(treeTableModel);
  81.  *  JScrollPane     scrollpane = new JScrollPane(treeTable);
  82.  * </pre>
  83.  * See {@link javax.swing.JTable} for an explanation of why putting the treetable
  84.  * inside a scroll pane is necessary.</p>
  85.  *
  86.  * <p>A single treetable model instance may be shared among more than one
  87.  * <code>JXTreeTable</code> instances. To access the treetable model, always call
  88.  * {@link #getTreeTableModel() getTreeTableModel} and
  89.  * {@link #setTreeTableModel(org.jdesktop.swingx.treetable.TreeTableModel) setTreeTableModel}.
  90.  * <code>JXTreeTable</code> wraps the supplied treetable model inside a private
  91.  * adapter class to adapt it to a {@link javax.swing.table.TableModel}. Although
  92.  * the model adapter is accessible through the {@link #getModel() getModel} method, you
  93.  * should avoid accessing and manipulating it in any way. In particular, each
  94.  * model adapter instance is tightly bound to a single table instance, and any
  95.  * attempt to share it with another table (for example, by calling
  96.  * {@link #setModel(javax.swing.table.TableModel) setModel})
  97.  * will throw an <code>IllegalArgumentException</code>!
  98.  *
  99.  * @author Philip Milne
  100.  * @author Scott Violet
  101.  * @author Ramesh Gupta
  102.  */
  103. public class JXTreeTable extends JXTable {
  104.     /**
  105.      * Renderer used to render cells within the
  106.      *  {@link #isHierarchical(int) hierarchical} column.
  107.      *  renderer extends JXTree and implements TableCellRenderer
  108.      */
  109.     private TreeTableCellRenderer renderer;
  110.     /**
  111.      * Constructs a JXTreeTable using a
  112.      * {@link org.jdesktop.swingx.treetable.DefaultTreeTableModel}.
  113.      */
  114.     public JXTreeTable() {
  115.         this(new DefaultTreeTableModel());
  116.     }
  117.     /**
  118.      * Constructs a JXTreeTable using the specified
  119.      * {@link org.jdesktop.swingx.treetable.TreeTableModel}.
  120.      *
  121.      * @param treeModel model for the JXTreeTable
  122.      */
  123.     public JXTreeTable(TreeTableModel treeModel) {
  124.         // Implementation note:
  125.         // Make sure that the SAME instance of treeModel is passed to the
  126.         // constructor for TreeTableCellRenderer as is passed in the first
  127.         // argument to the following chained constructor for this JXTreeTable:
  128.         this(treeModel, new JXTreeTable.TreeTableCellRenderer(treeModel));
  129.     }
  130.     /**
  131.      * Constructs a <code>JXTreeTable</code> using the specified
  132.      * {@link org.jdesktop.swing.treetable.TreeTableModel} and
  133.      * {@link org.jdesktop.swing.treetable.TreeTableCellRenderer}. The renderer
  134.      * must have been constructed using the same instance of
  135.      * {@link org.jdesktop.swing.treetable.TreeTableModel} as passed to this
  136.      * constructor.
  137.      *
  138.      * @param treeModel model for the JXTreeTable
  139.      * @param renderer cell renderer for the tree portion of this JXTreeTable instance.
  140.      * @throws IllegalArgumentException if an attempt is made to instantiate
  141.      * JXTreeTable and TreeTableCellRenderer with different instances of TreeTableModel.
  142.      */
  143.     private JXTreeTable(TreeTableModel treeModel, TreeTableCellRenderer renderer) {
  144.         // To avoid unnecessary object creation, such as the construction of a
  145.         // DefaultTableModel, it is better to invoke super(TreeTableModelAdapter)
  146.         // directly, instead of first invoking super() followed by a call to
  147.         // setTreeTableModel(TreeTableModel).
  148.         // Adapt tree model to table model before invoking super()
  149.         super(new TreeTableModelAdapter(treeModel, renderer));
  150.         // Enforce referential integrity; bail on fail
  151.         if (treeModel != renderer.getModel()) { // do not use assert here!
  152.             throw new IllegalArgumentException("Mismatched TreeTableModel");
  153.         }
  154.         // renderer-related initialization -- also called from setTreeTableModel()
  155.         init(renderer); // private method
  156.         initActions();
  157.         // disable sorting
  158.         super.setSortable(false);
  159.         // Install the default editor.
  160.         setDefaultEditor(AbstractTreeTableModel.hierarchicalColumnClass,
  161.             new TreeTableCellEditor(renderer));
  162.         // No grid.
  163.         setShowGrid(false); // superclass default is "true"
  164.         // Default intercell spacing
  165.         setIntercellSpacing(spacing); // for both row margin and column margin
  166.         // JTable supports row margins and intercell spacing, but JTree doesn't.
  167.         // We must reconcile the differences in the semantics of rowHeight as
  168.         // understood by JTable and JTree by overriding both setRowHeight() and
  169.         // setRowMargin();
  170.         adminSetRowHeight(getRowHeight());
  171.         setRowMargin(getRowMargin()); // call overridden setRowMargin()
  172.     }
  173.     private void initActions() {
  174.         // Register the actions that this class can handle.
  175.         ActionMap map = getActionMap();
  176.         map.put("expand-all", new Actions("expand-all"));
  177.         map.put("collapse-all", new Actions("collapse-all"));
  178.     }
  179.     /**
  180.      * A small class which dispatches actions.
  181.      * TODO: Is there a way that we can make this static?
  182.      */
  183.     private class Actions extends UIAction {
  184.         Actions(String name) {
  185.             super(name);
  186.         }
  187.         public void actionPerformed(ActionEvent evt) {
  188.             if ("expand-all".equals(getName())) {
  189.         expandAll();
  190.             }
  191.             else if ("collapse-all".equals(getName())) {
  192.                 collapseAll();
  193.             }
  194.         }
  195.     }
  196.     /** 
  197.      * overridden to do nothing. 
  198.      * 
  199.      * TreeTable is not sortable by default, because 
  200.      * Sorters/Filters currently don't work properly.
  201.      * 
  202.      */
  203.     public void setSortable(boolean sortable) {
  204.         // no-op
  205.     }
  206.     /**
  207.      * <p>Sets whether the table draws horizontal lines between cells. It draws
  208.      * the lines if <code>show</code> is true; otherwise it doesn't. By default,
  209.      * a table draws the lines.</p>
  210.      *
  211.      * <p>If you want the lines to be drawn, make sure that the row margin or
  212.      * horizontal intercell spacing is greater than zero.</p>
  213.      *
  214.      * @param show true, if horizontal lines should be drawn; false, if lines
  215.      * should not be drawn
  216.      * @see javax.swing.JTable#getShowHorizontalLines() getShowHorizontalLines
  217.      * @see #setRowMargin(int) setRowMargin
  218.      * @see javax.swing.JTable#setIntercellSpacing(java.awt.Dimension) setIntercellSpacing
  219.      */
  220.     public void setShowHorizontalLines(boolean show) {
  221.         super.setShowHorizontalLines(show);
  222.     }
  223.     /**
  224.      * <p>Sets whether the table draws vertical lines between cells. It draws
  225.      * the lines if <code>show</code> is true; otherwise it doesn't. By default,
  226.      * a table draws the lines.</p>
  227.      *
  228.      * <p>If you want the lines to be drawn, make sure that the column margin or
  229.      * vertical intercell spacing is greater than zero.</p>
  230.      *
  231.      * @param show true, if vertical lines should be drawn; false, if lines
  232.      * should not be drawn
  233.      * @see javax.swing.JTable#getShowVerticalLines() getShowVerticalLines
  234.      * @see #setColumnMargin(int) setColumnMargin
  235.      * @see javax.swing.JTable#setIntercellSpacing(java.awt.Dimension) setIntercellSpacing
  236.      */
  237.     public void setShowVerticalLines(boolean show) {
  238.         super.setShowVerticalLines(show);
  239.     }
  240.     /**
  241.      * Overriden to invoke repaint for the particular location if
  242.      * the column contains the tree. This is done as the tree editor does
  243.      * not fill the bounds of the cell, we need the renderer to paint
  244.      * the tree in the background, and then draw the editor over it.
  245.      * You should not need to call this method directly.
  246.      *
  247.      * {@inheritDoc}
  248.      */
  249.     public boolean editCellAt(int row, int column, EventObject e) {
  250.         expandOrCollapseNode(e);    // RG: Fix Issue 49!
  251.         boolean canEdit = super.editCellAt(row, column, e);
  252.         if (canEdit && isHierarchical(column)) {
  253.             repaint(getCellRect(row, column, false));
  254.         }
  255.         return canEdit;
  256.     }
  257.     private void expandOrCollapseNode(EventObject e) {
  258.         if (e instanceof MouseEvent) {
  259.             MouseEvent me = (MouseEvent) e;
  260.             // If the modifiers are not 0 (or the left mouse button),
  261.             // tree may try and toggle the selection, and table
  262.             // will then try and toggle, resulting in the
  263.             // selection remaining the same. To avoid this, we
  264.             // only dispatch when the modifiers are 0 (or the left mouse
  265.             // button).
  266.             if (me.getModifiers() == 0 ||
  267.                 me.getModifiers() == InputEvent.BUTTON1_MASK) {
  268.                 final int count = getColumnCount();
  269.                 for (int i = count - 1; i >= 0; i--) {
  270.                     if (isHierarchical(i)) {
  271.                         
  272.                         int savedHeight = renderer.getRowHeight();
  273.                         renderer.setRowHeight(getRowHeight());
  274.                         MouseEvent pressed = new MouseEvent
  275.                             (renderer,
  276.                              me.getID(),
  277.                              me.getWhen(),
  278.                              me.getModifiers(),
  279.                              me.getX() - getCellRect(0, i, false).x,
  280.                              me.getY(),
  281.                              me.getClickCount(),
  282.                              me.isPopupTrigger());
  283.                         renderer.dispatchEvent(pressed);
  284.                         // For Mac OS X, we need to dispatch a MOUSE_RELEASED as well
  285.                         MouseEvent released = new MouseEvent
  286.                             (renderer,
  287.                              java.awt.event.MouseEvent.MOUSE_RELEASED,
  288.                              pressed.getWhen(),
  289.                              pressed.getModifiers(),
  290.                              pressed.getX(),
  291.                              pressed.getY(),
  292.                              pressed.getClickCount(),
  293.                              pressed.isPopupTrigger());
  294.                         renderer.dispatchEvent(released);
  295.                         renderer.setRowHeight(savedHeight);
  296.                         break;
  297.                     }
  298.                 }
  299.             }
  300.         }
  301.     }
  302.     /**
  303.      * Overridden to provide a workaround for BasicTableUI anomaly. Make sure
  304.      * the UI never tries to resize the editor. The UI currently uses different
  305.      * techniques to paint the renderers and editors. So, overriding setBounds()
  306.      * is not the right thing to do for an editor. Returning -1 for the
  307.      * editing row in this case, ensures the editor is never painted.
  308.      *
  309.      * {@inheritDoc}
  310.      */
  311.     public int getEditingRow() {
  312.         return isHierarchical(editingColumn) ? -1 : editingRow;
  313.     }
  314.     /**
  315.      * Returns the actual row that is editing as <code>getEditingRow</code>
  316.      * will always return -1.
  317.      */
  318.     private int realEditingRow() {
  319.         return editingRow;
  320.     }
  321.     /**
  322.      * Sets the data model for this JXTreeTable to the specified
  323.      * {@link org.jdesktop.swingx.treetable.TreeTableModel}. The same data model
  324.      * may be shared by any number of JXTreeTable instances.
  325.      *
  326.      * @param treeModel data model for this JXTreeTable
  327.      */
  328.     public void setTreeTableModel(TreeTableModel treeModel) {
  329.         renderer.setModel(treeModel);
  330.         // #241: make sure old listeners are removed
  331.         // JW: this should be done more cleanly, actually there is no need
  332.         // to create a new adapter when setting a new treeModel.
  333.         // what's needed is to fire a structureChanged
  334.         
  335.         ((TreeTableModelAdapter)getModel()).setTreeTableModel(treeModel);
  336.         // Enforce referential integrity; bail on fail
  337.         if (treeModel != renderer.getModel()) { // do not use assert here!
  338.             throw new IllegalArgumentException("Mismatched TreeTableModel");
  339.         }
  340.         // Install the default editor.
  341.         setDefaultEditor(AbstractTreeTableModel.hierarchicalColumnClass,
  342.             new TreeTableCellEditor(renderer));
  343.         // JTable supports row margins and intercell spacing, but JTree doesn't.
  344.         // We must reconcile the differences in the semantics of rowHeight as
  345.         // understood by JTable and JTree by overriding both setRowHeight() and
  346.         // setRowMargin();
  347.         adminSetRowHeight(getRowHeight());
  348.         setRowMargin(getRowMargin()); // call overridden setRowMargin()
  349.     }
  350.     /**
  351.      * Returns the underlying TreeTableModel for this JXTreeTable.
  352.      *
  353.      * @return the underlying TreeTableModel for this JXTreeTable
  354.      */
  355.     public TreeTableModel getTreeTableModel() {
  356.         return ((TreeTableModelAdapter) getModel()).getTreeTableModel();
  357.     }
  358.     /**
  359.      * <p>Overrides superclass version to make sure that the specified
  360.      * {@link javax.swing.table.TableModel} is compatible with JXTreeTable before
  361.      * invoking the inherited version.</p>
  362.      *
  363.      * <p>Because JXTreeTable internally adapts an
  364.      * {@link org.jdesktop.swingx.treetable.TreeTableModel} to make it a compatible
  365.      * TableModel, <b>this method should never be called directly</b>. Use
  366.      * {@link #setTreeTableModel(org.jdesktop.swingx.treetable.TreeTableModel) setTreeTableModel} instead.</p>
  367.      *
  368.      * <p>While it is possible to obtain a reference to this adapted
  369.      * version of the TableModel by calling {@link javax.swing.JTable#getModel()},
  370.      * any attempt to call setModel() with that adapter will fail because
  371.      * the adapter might have been bound to a different JXTreeTable instance. If
  372.      * you want to extract the underlying TreeTableModel, which, by the way,
  373.      * <em>can</em> be shared, use {@link #getTreeTableModel() getTreeTableModel}
  374.      * instead</p>.
  375.      *
  376.      * @param tableModel must be a TreeTableModelAdapter
  377.      * @throws IllegalArgumentException if the specified tableModel is not an
  378.      * instance of TreeTableModelAdapter
  379.      */
  380.     public final void setModel(TableModel tableModel) { // note final keyword
  381.         if (tableModel instanceof TreeTableModelAdapter) {
  382.             if (((TreeTableModelAdapter) tableModel).getTreeTable() == null) {
  383.                 // Passing the above test ensures that this method is being
  384.                 // invoked either from JXTreeTable/JTable constructor or from
  385.                 // setTreeTableModel(TreeTableModel)
  386.                 super.setModel(tableModel); // invoke superclass version
  387.                 ((TreeTableModelAdapter) tableModel).bind(this); // permanently bound
  388.                 // Once a TreeTableModelAdapter is bound to any JXTreeTable instance,
  389.                 // invoking JXTreeTable.setModel() with that adapter will throw an
  390.                 // IllegalArgumentException, because we really want to make sure
  391.                 // that a TreeTableModelAdapter is NOT shared by another JXTreeTable.
  392.             }
  393.             else {
  394.                 throw new IllegalArgumentException("model already bound");
  395.             }
  396.         }
  397.         else {
  398.             throw new IllegalArgumentException("unsupported model type");
  399.         }
  400.     }
  401.     /**
  402.      * Throws UnsupportedOperationException because variable height rows are
  403.      * not supported.
  404.      *
  405.      * @param row ignored
  406.      * @param rowHeight ignored
  407.      * @throws UnsupportedOperationException because variable height rows are
  408.      * not supported
  409.      */
  410.     public final void setRowHeight(int row, int rowHeight) {
  411.         throw new UnsupportedOperationException("variable height rows not supported");
  412.     }
  413.     /**
  414.      * Sets the row height for this JXTreeTable. Reconciles semantic differences
  415.      * between JTable and JTree regarding row height.
  416.      *
  417.      * @param rowHeight height of a row
  418.      */
  419.     public void setRowHeight(int rowHeight) {
  420.         super.setRowHeight(rowHeight);
  421.         adjustTreeRowHeight(); // JTree doesn't have setRowMargin. So adjust.
  422.     }
  423.     /**
  424.      * <p>Sets the margin between columns.</p>
  425.      *
  426.      * <p>If you set the column margin to zero, make sure that you also set
  427.      * <code>showVerticalLines</code> to <code>false</code>.</p>
  428.      *
  429.      * @param columnMargin margin between columns; must be greater than or equal to zero.
  430.      * @see #setShowVerticalLines(boolean) setShowVerticalLines
  431.      */
  432.     public void setColumnMargin(int columnMargin) {
  433.         super.setColumnMargin(columnMargin);
  434.     }
  435.     /**
  436.      * <p>Overridden to ensure that private renderer state is kept in sync with the
  437.      * state of the component. Calls the inherited version after performing the
  438.      * necessary synchronization. If you override this method, make sure you call
  439.      * this version from your version of this method.</p>
  440.      *
  441.      * <p>If you set row margin to zero, make sure that you also set
  442.      * <code>showHorizontalLines</code> to <code>false</code>.</p>
  443.      *
  444.      * @param rowMargin margin or intercell spacing between rows
  445.      * @see #setShowHorizontalLines(boolean) setShowHorizontalLines
  446.      */
  447.     public void setRowMargin(int rowMargin) {
  448.         // No need to override setIntercellSpacing, because the change in
  449.         // rowMargin will be funneled through this method anyway.
  450.         super.setRowMargin(rowMargin);
  451.         adjustTreeRowHeight(); // JTree doesn't have setRowMargin. So adjust.
  452.     }
  453.     /**
  454.      * Reconciles semantic differences between JTable and JTree regarding
  455.      * row height.
  456.      */
  457.     private void adjustTreeRowHeight() {
  458.         final int treeRowHeight = rowHeight + (rowMargin << 1);
  459.         if (renderer != null && renderer.getRowHeight() != treeRowHeight) {
  460.             renderer.setRowHeight(treeRowHeight);
  461.         }
  462.     }
  463.     /**
  464.      * <p>Overridden to ensure that private renderer state is kept in sync with the
  465.      * state of the component. Calls the inherited version after performing the
  466.      * necessary synchronization. If you override this method, make sure you call
  467.      * this version from your version of this method.</p>
  468.      *
  469.      * <p>This version maps the selection mode used by the renderer to match the
  470.      * selection mode specified for the table. Specifically, the modes are mapped
  471.      * as follows:
  472.      * <pre>
  473.      *  ListSelectionModel.SINGLE_INTERVAL_SELECTION: TreeSelectionModel.CONTIGUOUS_TREE_SELECTION;
  474.      *  ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
  475.      *  any other (default): TreeSelectionModel.SINGLE_TREE_SELECTION;
  476.      * </pre>
  477.      *
  478.      * {@inheritDoc}
  479.      *
  480.      * @param mode any of the table selection modes
  481.      */
  482.     public void setSelectionMode(int mode) {
  483.         if (renderer != null) {
  484.             switch (mode) {
  485.                 case ListSelectionModel.SINGLE_INTERVAL_SELECTION: {
  486.                     renderer.getSelectionModel().setSelectionMode(
  487.                         TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
  488.                     break;
  489.                 }
  490.                 case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: {
  491.                     renderer.getSelectionModel().setSelectionMode(
  492.                         TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
  493.                     break;
  494.                 }
  495.                 default: {
  496.                     renderer.getSelectionModel().setSelectionMode(
  497.                         TreeSelectionModel.SINGLE_TREE_SELECTION);
  498.                     break;
  499.                 }
  500.             }
  501.         }
  502.         super.setSelectionMode(mode);
  503.     }
  504.     /**
  505.      * Overrides superclass version to provide support for cell decorators.
  506.      *
  507.      * @param renderer the <code>TableCellRenderer</code> to prepare
  508.      * @param row the row of the cell to render, where 0 is the first row
  509.      * @param column the column of the cell to render, where 0 is the first column
  510.      * @return the <code>Component</code> used as a stamp to render the specified cell
  511.      */
  512.     public Component prepareRenderer(TableCellRenderer renderer, int row,
  513.         int column) {
  514.         
  515.         Component component = super.prepareRenderer(renderer, row, column);
  516.         // MUST ALWAYS ACCESS dataAdapter through accessor method!!!
  517.         ComponentAdapter    adapter = getComponentAdapter();
  518.         adapter.row = row;
  519.         adapter.column = column;
  520.         
  521.         return applyRenderer(component, adapter); 
  522.     }
  523.     /**
  524.      * Performs necessary housekeeping before the renderer is actually applied.
  525.      *
  526.      * @param component
  527.      * @param adapter component data adapter
  528.      * @throws NullPointerException if the specified component or adapter is null
  529.      */
  530.     protected Component applyRenderer(Component component,
  531.         ComponentAdapter adapter) {
  532.         if (component == null) {
  533.             throw new IllegalArgumentException("null component");
  534.         }
  535.         if (adapter == null) {
  536.             throw new IllegalArgumentException("null component data adapter");
  537.         }
  538.         if (isHierarchical(adapter.column)) {
  539.             // After all decorators have been applied, make sure that relevant
  540.             // attributes of the table cell renderer are applied to the
  541.             // tree cell renderer before the hierarchical column is rendered!
  542.             TreeCellRenderer tcr = renderer.getCellRenderer();
  543.             if (tcr instanceof JXTree.DelegatingRenderer) {
  544.                 tcr = ((JXTree.DelegatingRenderer) tcr).getDelegateRenderer();
  545.                 
  546.             }
  547.             if (tcr instanceof DefaultTreeCellRenderer) {
  548.                 DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr);
  549.                 if (adapter.isSelected()) {
  550.                     dtcr.setTextSelectionColor(component.getForeground());
  551.                     dtcr.setBackgroundSelectionColor(component.getBackground());
  552.                } else {
  553.                     dtcr.setTextNonSelectionColor(component.getForeground());
  554.                     dtcr.setBackgroundNonSelectionColor(component.getBackground());
  555.                 }
  556.             } 
  557.         }
  558.         return component;
  559.     }
  560.     /**
  561.      * Sets the specified TreeCellRenderer as the Tree cell renderer.
  562.      *
  563.      * @param cellRenderer to use for rendering tree cells.
  564.      */
  565.     public void setTreeCellRenderer(TreeCellRenderer cellRenderer) {
  566.         if (renderer != null) {
  567.             renderer.setCellRenderer(cellRenderer);
  568.         }
  569.     }
  570.     public TreeCellRenderer getTreeCellRenderer() {
  571.         return renderer.getCellRenderer();
  572.     }
  573.     
  574.     public String getToolTipText(MouseEvent event) {
  575.         int column = columnAtPoint(event.getPoint());
  576.         if (isHierarchical(column)) {
  577.             return renderer.getToolTipText(event);
  578.         }
  579.         return super.getToolTipText(event);
  580.     }
  581.     
  582.     /**
  583.      * Sets the specified icon as the icon to use for rendering collapsed nodes.
  584.      *
  585.      * @param icon to use for rendering collapsed nodes
  586.      */
  587.     public void setCollapsedIcon(Icon icon) {
  588.         renderer.setCollapsedIcon(icon);
  589.     }
  590.     /**
  591.      * Sets the specified icon as the icon to use for rendering expanded nodes.
  592.      *
  593.      * @param icon to use for rendering expanded nodes
  594.      */
  595.     public void setExpandedIcon(Icon icon) {
  596.         renderer.setExpandedIcon(icon);
  597.     }
  598.     /**
  599.      * Sets the specified icon as the icon to use for rendering open container nodes.
  600.      *
  601.      * @param icon to use for rendering open nodes
  602.      */
  603.     public void setOpenIcon(Icon icon) {
  604.         renderer.setOpenIcon(icon);
  605.     }
  606.     /**
  607.      * Sets the specified icon as the icon to use for rendering closed container nodes.
  608.      *
  609.      * @param icon to use for rendering closed nodes
  610.      */
  611.     public void setClosedIcon(Icon icon) {
  612.         renderer.setClosedIcon(icon);
  613.     }
  614.     /**
  615.      * Sets the specified icon as the icon to use for rendering leaf nodes.
  616.      *
  617.      * @param icon to use for rendering leaf nodes
  618.      */
  619.     public void setLeafIcon(Icon icon) {
  620.         renderer.setLeafIcon(icon);
  621.     }
  622.     /**
  623.      * Overridden to ensure that private renderer state is kept in sync with the
  624.      * state of the component. Calls the inherited version after performing the
  625.      * necessary synchronization. If you override this method, make sure you call
  626.      * this version from your version of this method.
  627.      */
  628.     public void clearSelection() {
  629.         if (renderer != null) {
  630.             renderer.clearSelection();
  631.         }
  632.         super.clearSelection();
  633.     }
  634.     /**
  635.      * Collapses all nodes in the treetable.
  636.      */
  637.     public void collapseAll() {
  638.         renderer.collapseAll();
  639.     }
  640.     /**
  641.      * Expands all nodes in the treetable.
  642.      */
  643.     public void expandAll() {
  644.         renderer.expandAll();
  645.     }
  646.     /**
  647.      * Collapses the node at the specified path in the treetable.
  648.      *
  649.      * @param path path of the node to collapse
  650.      */
  651.     public void collapsePath(TreePath path) {
  652.         renderer.collapsePath(path);
  653.     }
  654.     /**
  655.      * Expands the the node at the specified path in the treetable.
  656.      *
  657.      * @param path path of the node to expand
  658.      */
  659.     public void expandPath(TreePath path) {
  660.         renderer.expandPath(path);
  661.     }
  662.     /**
  663.      * Collapses the row in the treetable. If the specified row index is
  664.      * not valid, this method will have no effect.
  665.      */
  666.     public void collapseRow(int row) {
  667.         renderer.collapseRow(row);
  668.     }
  669.     /**
  670.      * Expands the specified row in the treetable. If the specified row index is
  671.      * not valid, this method will have no effect.
  672.      */
  673.     public void expandRow(int row) {
  674.         renderer.expandRow(row);
  675.     }
  676.     /**
  677.      * Determines whether or not the root node from the TreeModel is visible.
  678.      *
  679.      * @param visible true, if the root node is visible; false, otherwise
  680.      */
  681.     public void setRootVisible(boolean visible) {
  682.         renderer.setRootVisible(visible);
  683.         repaint();
  684.     }
  685.     /**
  686.      * Returns true if the root node of the tree is displayed.
  687.      *
  688.      * @return true if the root node of the tree is displayed
  689.      */
  690.     public boolean isRootVisible() {
  691.         return renderer.isRootVisible();
  692.     }
  693.     /**
  694.      * Returns true if the value identified by path is currently viewable, which
  695.      * means it is either the root or all of its parents are expanded. Otherwise,
  696.      * this method returns false.
  697.      *
  698.      * @return true, if the value identified by path is currently viewable;
  699.      * false, otherwise
  700.      */
  701.     public boolean isVisible(TreePath path) {
  702.         return renderer.isVisible(path);
  703.     }
  704.     /**
  705.      * Returns true if the node identified by path is currently expanded.
  706.      * Otherwise, this method returns false.
  707.      *
  708.      * @param path path
  709.      * @return true, if the value identified by path is currently expanded;
  710.      * false, otherwise
  711.      */
  712.     public boolean isExpanded(TreePath path) {
  713.         return renderer.isExpanded(path);
  714.     }
  715.     /**
  716.      * Returns true if the node at the specified display row is currently expanded.
  717.      * Otherwise, this method returns false.
  718.      *
  719.      * @param row row
  720.      * @return true, if the node at the specified display row is currently expanded.
  721.      * false, otherwise
  722.      */
  723.     public boolean isExpanded(int row) {
  724.         return renderer.isExpanded(row);
  725.     }
  726.     /**
  727.      * Returns true if the node identified by path is currently collapsed, 
  728.      * this will return false if any of the values in path are currently not 
  729.      * being displayed.   
  730.      *
  731.      * @param path path
  732.      * @return true, if the value identified by path is currently collapsed;
  733.      * false, otherwise
  734.      */
  735.     public boolean isCollapsed(TreePath path) {
  736.         return renderer.isCollapsed(path);
  737.     }
  738.     /**
  739.      * Returns true if the node at the specified display row is collapsed.
  740.      *
  741.      * @param row row
  742.      * @return true, if the node at the specified display row is currently collapsed.
  743.      * false, otherwise
  744.      */
  745.     public boolean isCollapsed(int row) {
  746.         return renderer.isCollapsed(row);
  747.     }
  748.     
  749.     /**
  750.      * Returns an <code>Enumeration</code> of the descendants of the
  751.      * path <code>parent</code> that
  752.      * are currently expanded. If <code>parent</code> is not currently
  753.      * expanded, this will return <code>null</code>.
  754.      * If you expand/collapse nodes while
  755.      * iterating over the returned <code>Enumeration</code>
  756.      * this may not return all
  757.      * the expanded paths, or may return paths that are no longer expanded.
  758.      *
  759.      * @param parent  the path which is to be examined
  760.      * @return an <code>Enumeration</code> of the descendents of 
  761.      * <code>parent</code>, or <code>null</code> if
  762.      * <code>parent</code> is not currently expanded
  763.      */
  764.     
  765.     public Enumeration getExpandedDescendants(TreePath parent) {
  766.      return renderer.getExpandedDescendants(parent);
  767.     }
  768.     
  769.     /**
  770.      * Sets the value of the <code>expandsSelectedPaths</code> property for the tree
  771.      * part. This property specifies whether the selected paths should be expanded.
  772.      *
  773.      * @param expand true, if selected paths should be expanded; false, otherwise
  774.      */
  775.     public void setExpandsSelectedPaths(boolean expand) {
  776.         renderer.setExpandsSelectedPaths(expand);
  777.     }
  778.     /**
  779.      * Returns the value of the <code>expandsSelectedPaths</code> property.
  780.      *
  781.      * @return the value of the <code>expandsSelectedPaths</code> property
  782.      */
  783.     public boolean getExpandsSelectedPaths() {
  784.         return renderer.getExpandsSelectedPaths();
  785.     }
  786.     /**
  787.      * Returns the TreePath for a given x,y location.
  788.      *
  789.      * @param x x value
  790.      * @param y y value
  791.      *
  792.      * @return the <code>TreePath</code> for the givern location.
  793.      */
  794.      public TreePath getPathForLocation(int x, int y) {
  795.         int row = rowAtPoint(new Point(x,y));
  796.         if (row == -1) {
  797.           return null;  
  798.         }
  799.         return renderer.getPathForRow(row);
  800.      }
  801.     /**
  802.      * Returns the TreePath for a given row.
  803.      *
  804.      * @param row
  805.      *
  806.      * @return the <code>TreePath</code> for the given row.
  807.      */
  808.      public TreePath getPathForRow(int row) {
  809.         return renderer.getPathForRow(row);
  810.      }
  811.      /**
  812.       * Returns the row for a given TreePath.
  813.       *
  814.       * @param path
  815.       * @return the row for the given <code>TreePath</code>.
  816.       */
  817.      public int getRowForPath(TreePath path) {
  818.        return renderer.getRowForPath(path);
  819.      }
  820.     /**
  821.      * Sets the value of the <code>scrollsOnExpand</code> property for the tree
  822.      * part. This property specifies whether the expanded paths should be scrolled
  823.      * into view. In a look and feel in which a tree might not need to scroll
  824.      * when expanded, this property may be ignored.
  825.      *
  826.      * @param scroll true, if expanded paths should be scrolled into view;
  827.      * false, otherwise
  828.      */
  829.     public void setScrollsOnExpand(boolean scroll) {
  830.         renderer.setScrollsOnExpand(scroll);
  831.     }
  832.     /**
  833.      * Returns the value of the <code>scrollsOnExpand</code> property.
  834.      *
  835.      * @return the value of the <code>scrollsOnExpand</code> property
  836.      */
  837.     public boolean getScrollsOnExpand() {
  838.         return renderer.getScrollsOnExpand();
  839.     }
  840.     /**
  841.      * Sets the value of the <code>showsRootHandles</code> property for the tree
  842.      * part. This property specifies whether the node handles should be displayed.
  843.      * If handles are not supported by a particular look and feel, this property
  844.      * may be ignored.
  845.      *
  846.      * @param visible true, if root handles should be shown; false, otherwise
  847.      */
  848.     public void setShowsRootHandles(boolean visible) {
  849.         renderer.setShowsRootHandles(visible);
  850.         repaint();
  851.     }
  852.     /**
  853.      * Returns the value of the <code>showsRootHandles</code> property.
  854.      *
  855.      * @return the value of the <code>showsRootHandles</code> property
  856.      */
  857.     public boolean getShowsRootHandles() {
  858.         return renderer.getShowsRootHandles();
  859.     }
  860.     /**
  861.      * Adds a listener for <code>TreeExpansion</code> events.
  862.      * 
  863.      * TODO (JW): redirect event source to this. 
  864.      * 
  865.      * @param tel a TreeExpansionListener that will be notified 
  866.      * when a tree node is expanded or collapsed
  867.      */
  868.     public void addTreeExpansionListener(TreeExpansionListener tel) {
  869.         renderer.addTreeExpansionListener(tel);
  870.     }
  871.     /**
  872.      * Removes a listener for <code>TreeExpansion</code> events.
  873.      * @param tel the <code>TreeExpansionListener</code> to remove
  874.      */
  875.     public void removeTreeExpansionListener(TreeExpansionListener tel) {
  876.         renderer.removeTreeExpansionListener(tel);
  877.     }
  878.     /**
  879.      * Adds a listener for <code>TreeSelection</code> events.
  880.      * TODO (JW): redirect event source to this. 
  881.      * 
  882.      * @param tsl a TreeSelectionListener that will be notified 
  883.      * when a tree node is selected or deselected
  884.      */
  885.     public void addTreeSelectionListener(TreeSelectionListener tsl) {
  886.         renderer.addTreeSelectionListener(tsl);
  887.     }
  888.     /**
  889.      * Removes a listener for <code>TreeSelection</code> events.
  890.      * @param tsl the <code>TreeSelectionListener</code> to remove
  891.      */
  892.     public void removeTreeSelectionListener(TreeSelectionListener tsl) {
  893.         renderer.removeTreeSelectionListener(tsl);
  894.     }
  895.     /**
  896.      * Adds a listener for <code>TreeWillExpand</code> events.
  897.      * TODO (JW): redirect event source to this. 
  898.      * 
  899.      * @param tel a TreeWillExpandListener that will be notified 
  900.      * when a tree node will be expanded or collapsed 
  901.      */
  902.     public void addTreeWillExpandListener(TreeWillExpandListener tel) {
  903.         renderer.addTreeWillExpandListener(tel);
  904.     }
  905.     /**
  906.      * Removes a listener for <code>TreeWillExpand</code> events.
  907.      * @param tel the <code>TreeWillExpandListener</code> to remove
  908.      */
  909.     public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
  910.         renderer.removeTreeWillExpandListener(tel);
  911.      }
  912.  
  913.     
  914.     /**
  915.      * Returns the selection model for the tree portion of the this treetable.
  916.      *
  917.      * @return selection model for the tree portion of the this treetable
  918.      */
  919.     public TreeSelectionModel getTreeSelectionModel() {
  920.         return renderer.getSelectionModel();    // RG: Fix JDNC issue 41
  921.     }
  922.     /**
  923.      * Overriden to invoke supers implementation, and then,
  924.      * if the receiver is editing a Tree column, the editors bounds is
  925.      * reset. The reason we have to do this is because JTable doesn't
  926.      * think the table is being edited, as <code>getEditingRow</code> returns
  927.      * -1, and therefore doesn't automaticly resize the editor for us.
  928.      */
  929.     public void sizeColumnsToFit(int resizingColumn) {
  930.         /** @todo Review wrt doLayout() */
  931.         super.sizeColumnsToFit(resizingColumn);
  932.         // rg:changed
  933.         if (getEditingColumn() != -1 && isHierarchical(editingColumn)) {
  934.             Rectangle cellRect = getCellRect(realEditingRow(),
  935.                 getEditingColumn(), false);
  936.             Component component = getEditorComponent();
  937.             component.setBounds(cellRect);
  938.             component.validate();
  939.         }
  940.     }
  941.     /**
  942.      * Overridden to message super and forward the method to the tree.
  943.      * Since the tree is not actually in the component hieachy it will
  944.      * never receive this unless we forward it in this manner.
  945.      */
  946.     public void updateUI() {
  947.         super.updateUI();
  948.         if (renderer != null) {
  949.             //  final int savedHeight = renderer.getRowHeight();
  950.             renderer.updateUI();
  951.             //  renderer.setRowHeight(savedHeight);
  952.             // Do this so that the editor is referencing the current renderer
  953.             // from the tree. The renderer can potentially change each time
  954.             // laf changes.
  955.             setDefaultEditor(AbstractTreeTableModel.hierarchicalColumnClass,
  956.                 new TreeTableCellEditor(renderer));
  957.             if (getBackground() == null || getBackground()instanceof UIResource) {
  958.                 setBackground(renderer.getBackground());
  959.             }
  960.         }
  961.     }
  962.     /**
  963.      * Determines if the specified column contains hierarchical nodes.
  964.      *
  965.      * @param column zero-based index of the column
  966.      * @return true if the class of objects in the specified column implement
  967.      * the {@link javax.swing.tree.TreeNode} interface; false otherwise.
  968.      */
  969.     public boolean isHierarchical(int column) {
  970.         return AbstractTreeTableModel.hierarchicalColumnClass.isAssignableFrom(
  971.             getColumnClass(column));
  972.     }
  973.     /**
  974.      * Initializes this JXTreeTable and permanently binds the specified renderer
  975.      * to it.
  976.      *
  977.      * @param renderer private tree/renderer permanently and exclusively bound
  978.      * to this JXTreeTable.
  979.      */
  980.     private final void init(TreeTableCellRenderer renderer) {
  981.         this.renderer = renderer;
  982.         // Force the JTable and JTree to share their row selection models.
  983.         ListToTreeSelectionModelWrapper selectionWrapper =
  984.             new ListToTreeSelectionModelWrapper();
  985.         // JW: when would that happen?
  986.         if (renderer != null) {
  987.             renderer.bind(this); // IMPORTANT: link back!
  988.             renderer.setSelectionModel(selectionWrapper);
  989.         }
  990.         setSelectionModel(selectionWrapper.getListSelectionModel());
  991.         setDefaultRenderer(AbstractTreeTableModel.hierarchicalColumnClass,
  992.             renderer);
  993.         
  994.         // propagate the lineStyle property to the renderer
  995.         PropertyChangeListener l = new PropertyChangeListener() {
  996.             public void propertyChange(PropertyChangeEvent evt) {
  997.                 JXTreeTable.this.renderer.putClientProperty(evt.getPropertyName(), evt.getNewValue());
  998.                 
  999.             }
  1000.             
  1001.         };
  1002.         addPropertyChangeListener("JTree.lineStyle", l);
  1003.         
  1004.     }
  1005.     /**
  1006.      * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
  1007.      * to listen for changes in the ListSelectionModel it maintains. Once
  1008.      * a change in the ListSelectionModel happens, the paths are updated
  1009.      * in the DefaultTreeSelectionModel.
  1010.      */
  1011.     class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel {
  1012.         /** Set to true when we are updating the ListSelectionModel. */
  1013.         protected boolean updatingListSelectionModel;
  1014.         public ListToTreeSelectionModelWrapper() {
  1015.             super();
  1016.             getListSelectionModel().addListSelectionListener
  1017.                 (createListSelectionListener());
  1018.         }
  1019.         /**
  1020.          * Returns the list selection model. ListToTreeSelectionModelWrapper
  1021.          * listens for changes to this model and updates the selected paths
  1022.          * accordingly.
  1023.          */
  1024.         ListSelectionModel getListSelectionModel() {
  1025.             return listSelectionModel;
  1026.         }
  1027.         /**
  1028.          * This is overridden to set <code>updatingListSelectionModel</code>
  1029.          * and message super. This is the only place DefaultTreeSelectionModel
  1030.          * alters the ListSelectionModel.
  1031.          */
  1032.         public void resetRowSelection() {
  1033.             if (!updatingListSelectionModel) {
  1034.                 updatingListSelectionModel = true;
  1035.                 try {
  1036.                     super.resetRowSelection();
  1037.                 }
  1038.                 finally {
  1039.                     updatingListSelectionModel = false;
  1040.                 }
  1041.             }
  1042.             // Notice how we don't message super if
  1043.             // updatingListSelectionModel is true. If
  1044.             // updatingListSelectionModel is true, it implies the
  1045.             // ListSelectionModel has already been updated and the
  1046.             // paths are the only thing that needs to be updated.
  1047.         }
  1048.         /**
  1049.          * Creates and returns an instance of ListSelectionHandler.
  1050.          */
  1051.         protected ListSelectionListener createListSelectionListener() {
  1052.             return new ListSelectionHandler();
  1053.         }
  1054.         /**
  1055.          * If <code>updatingListSelectionModel</code> is false, this will
  1056.          * reset the selected paths from the selected rows in the list
  1057.          * selection model.
  1058.          */
  1059.         protected void updateSelectedPathsFromSelectedRows() {
  1060.             if (!updatingListSelectionModel) {
  1061.                 updatingListSelectionModel = true;
  1062.                 try {
  1063.                     // This is way expensive, ListSelectionModel needs an
  1064.                     // enumerator for iterating.
  1065.                     int min = listSelectionModel.getMinSelectionIndex();
  1066.                     int max = listSelectionModel.getMaxSelectionIndex();
  1067.                     clearSelection();
  1068.                     if (min != -1 && max != -1) {
  1069.                         for (int counter = min; counter <= max; counter++) {
  1070.                             if (listSelectionModel.isSelectedIndex(counter)) {
  1071.                                 TreePath selPath = renderer.getPathForRow(
  1072.                                     counter);
  1073.                                 if (selPath != null) {
  1074.                                     addSelectionPath(selPath);
  1075.                                 }
  1076.                             }
  1077.                         }
  1078.                     }
  1079.                 }
  1080.                 finally {
  1081.                     updatingListSelectionModel = false;
  1082.                 }
  1083.             }
  1084.         }
  1085.         /**
  1086.          * Class responsible for calling updateSelectedPathsFromSelectedRows
  1087.          * when the selection of the list changse.
  1088.          */
  1089.         class ListSelectionHandler implements ListSelectionListener {
  1090.             public void valueChanged(ListSelectionEvent e) {
  1091.                 updateSelectedPathsFromSelectedRows();
  1092.             }
  1093.         }
  1094.     }
  1095.     private static class TreeTableModelAdapter extends AbstractTableModel {
  1096.         private TreeModelListener treeModelListener;
  1097.         /**
  1098.          * Maintains a TreeTableModel and a JTree as purely implementation details.
  1099.          * Developers can plug in any type of custom TreeTableModel through a
  1100.          * JXTreeTable constructor or through setTreeTableModel().
  1101.          *
  1102.          * @param model Underlying data model for the JXTreeTable that will ultimately
  1103.          * be bound to this TreeTableModelAdapter
  1104.          * @param tree TreeTableCellRenderer instantiated with the same model as
  1105.          * specified by the model parameter of this constructor
  1106.          * @throws IllegalArgumentException if a null model argument is passed
  1107.          * @throws IllegalArgumentException if a null tree argument is passed
  1108.          */
  1109.         TreeTableModelAdapter(TreeTableModel model, JTree tree) {
  1110.             assert model != null;
  1111.             assert tree != null;
  1112.             this.tree = tree; // need tree to implement getRowCount()
  1113.             setTreeTableModel(model);
  1114.             tree.addTreeExpansionListener(new TreeExpansionListener() {
  1115.                 // Don't use fireTableRowsInserted() here; the selection model
  1116.                 // would get updated twice.
  1117.                 public void treeExpanded(TreeExpansionEvent event) {
  1118.                     fireTableDataChanged();
  1119.                 }
  1120.                 public void treeCollapsed(TreeExpansionEvent event) {
  1121.                     fireTableDataChanged();
  1122.                 }
  1123.             });
  1124.         }
  1125.         /**
  1126.          * 
  1127.          * @param model must not be null!
  1128.          */
  1129.         public void setTreeTableModel(TreeTableModel model) {
  1130.             TreeTableModel old = getTreeTableModel();
  1131.             if (old != null) {
  1132.                 old.removeTreeModelListener(getTreeModelListener());
  1133.             }
  1134.             this.model = model;
  1135.             // Install a TreeModelListener that can update the table when
  1136.             // tree changes. 
  1137.             model.addTreeModelListener(getTreeModelListener());
  1138.             fireTableStructureChanged();
  1139.         }
  1140.         /**
  1141.          * @return
  1142.          */
  1143.         private TreeModelListener getTreeModelListener() {
  1144.             if (treeModelListener == null) {
  1145.                 treeModelListener = new TreeModelListener() {
  1146.                     // We use delayedFireTableDataChanged as we can
  1147.                     // not be guaranteed the tree will have finished processing
  1148.                     // the event before us.
  1149.                     public void treeNodesChanged(TreeModelEvent e) {
  1150.                         delayedFireTableDataChanged(e, 0);
  1151.                     }
  1152.                     public void treeNodesInserted(TreeModelEvent e) {
  1153.                         delayedFireTableDataChanged(e, 1);
  1154.                     }
  1155.                     public void treeNodesRemoved(TreeModelEvent e) {
  1156.                         delayedFireTableDataChanged(e, 2);
  1157.                     }
  1158.                     public void treeStructureChanged(TreeModelEvent e) {
  1159.                         delayedFireTableDataChanged();
  1160.                     }
  1161.                 };
  1162.             }
  1163.             return treeModelListener;
  1164.         }
  1165.         /**
  1166.          * Returns the real TreeTableModel that is wrapped by this TreeTableModelAdapter.
  1167.          *
  1168.          * @return the real TreeTableModel that is wrapped by this TreeTableModelAdapter
  1169.          */
  1170.         public TreeTableModel getTreeTableModel() {
  1171.             return model;
  1172.         }
  1173.         /**
  1174.          * Returns the JXTreeTable instance to which this TreeTableModelAdapter is
  1175.          * permanently and exclusively bound. For use by
  1176.          * {@link org.jdesktop.swingx.JXTreeTable#setModel(javax.swing.table.TableModel)}.
  1177.          *
  1178.          * @return JXTreeTable to which this TreeTableModelAdapter is permanently bound
  1179.          */
  1180.         protected JXTreeTable getTreeTable() {
  1181.             return treeTable;
  1182.         }
  1183.         /**
  1184.          * Immutably binds this TreeTableModelAdapter to the specified JXTreeTable.
  1185.          *
  1186.          * @param treeTable the JXTreeTable instance that this adapter is bound to.
  1187.          */
  1188.         protected final void bind(JXTreeTable treeTable) {
  1189.             // Suppress potentially subversive invocation!
  1190.             // Prevent clearing out the deck for possible hijack attempt later!
  1191.             if (treeTable == null) {
  1192.                 throw new IllegalArgumentException("null treeTable");
  1193.             }
  1194.             if (this.treeTable == null) {
  1195.                 this.treeTable = treeTable;
  1196.             }
  1197.             else {
  1198.                 throw new IllegalArgumentException("adapter already bound");
  1199.             }
  1200.         }
  1201.         // Wrappers, implementing TableModel interface.
  1202.         // TableModelListener management provided by AbstractTableModel superclass.
  1203.         public Class getColumnClass(int column) {
  1204.             return model.getColumnClass(column);
  1205.         }
  1206.         public int getColumnCount() {
  1207.             return model.getColumnCount();
  1208.         }
  1209.         public String getColumnName(int column) {
  1210.             return model.getColumnName(column);
  1211.         }
  1212.         public int getRowCount() {
  1213.             return tree.getRowCount();
  1214.         }
  1215.         public Object getValueAt(int row, int column) {
  1216.             return model.getValueAt(nodeForRow(row), column);
  1217.         }
  1218.         public boolean isCellEditable(int row, int column) {
  1219.             return model.isCellEditable(nodeForRow(row), column);
  1220.         }
  1221.         public void setValueAt(Object value, int row, int column) {
  1222.             model.setValueAt(value, nodeForRow(row), column);
  1223.         }
  1224.         protected Object nodeForRow(int row) {
  1225.             return tree.getPathForRow(row).getLastPathComponent();
  1226.         }
  1227.         /**
  1228.          * Invokes fireTableDataChanged after all the pending events have been
  1229.          * processed. SwingUtilities.invokeLater is used to handle this.
  1230.          */
  1231.         private void delayedFireTableDataChanged() {
  1232.             SwingUtilities.invokeLater(new Runnable() {
  1233.                 public void run() {
  1234.                     fireTableDataChanged();
  1235.                 }
  1236.             });
  1237.         }
  1238.         /**
  1239.          * Invokes fireTableDataChanged after all the pending events have been
  1240.          * processed. SwingUtilities.invokeLater is used to handle this.
  1241.          */
  1242.         private void delayedFireTableDataChanged(final TreeModelEvent tme, final int typeChange) {
  1243.             SwingUtilities.invokeLater(new Runnable() {
  1244.                 public void run() {
  1245.                     int indices[] = tme.getChildIndices();
  1246.                     TreePath path = tme.getTreePath();
  1247.                     if (indices != null) { 
  1248.                         if (tree.isExpanded(path)) { // Dont bother to update if the parent 
  1249.                                                     // node is collapsed
  1250.                             int startingRow = tree.getRowForPath(path)+1;
  1251.                             int min = Integer.MAX_VALUE;
  1252.                             int max = Integer.MIN_VALUE;
  1253.                             for (int i=0;i<indices.length;i++) {
  1254.                                 if (indices[i] < min) {
  1255.                                     min = indices[i];
  1256.                                 }
  1257.                                 if (indices[i] > max) {
  1258.                                     max = indices[i];
  1259.                                 }
  1260.                             }
  1261.                             switch (typeChange) {
  1262.                                 case 0 :
  1263.                                     fireTableRowsUpdated(startingRow + min, startingRow+max);
  1264.                                 break;
  1265.                                 case 1: 
  1266.                                     fireTableRowsInserted(startingRow + min, startingRow+max);
  1267.                                 break;
  1268.                                 case 2:
  1269.                                     fireTableRowsDeleted(startingRow + min, startingRow+max);
  1270.                                 break;
  1271.                             }
  1272.                         } else { 
  1273.                         // not expanded - but change might effect appearance of parent
  1274.                         // Issue #82-swingx
  1275.                             int row = tree.getRowForPath(path);
  1276.                             fireTableRowsUpdated(row, row);
  1277.                         }
  1278.                     }
  1279.                     else {  // case where the event is fired to identify root.
  1280.                         fireTableDataChanged();
  1281.                     }
  1282.                 }
  1283.             });
  1284.         }
  1285.         private TreeTableModel model; // immutable
  1286.         private final JTree tree; // immutable
  1287.         private JXTreeTable treeTable = null; // logically immutable
  1288.     }
  1289.     static class TreeTableCellRenderer extends JXTree implements
  1290.         TableCellRenderer {
  1291.         // Force user to specify TreeTableModel instead of more general TreeModel
  1292.         public TreeTableCellRenderer(TreeTableModel model) {
  1293.             super(model);
  1294.             putClientProperty("JTree.lineStyle", "None");
  1295.             setRootVisible(false); // superclass default is "true"
  1296.             setShowsRootHandles(true); // superclass default is "false"
  1297.                 /** @todo Support truncated text directly in DefaultTreeCellRenderer. */
  1298.             setOverwriteRendererIcons(true);
  1299.             setCellRenderer(new ClippedTreeCellRenderer());
  1300.         }
  1301.         /**
  1302.          * Immutably binds this TreeTableModelAdapter to the specified JXTreeTable.
  1303.          * For internal use by JXTreeTable only.
  1304.          *
  1305.          * @param treeTable the JXTreeTable instance that this renderer is bound to
  1306.          */
  1307.         public final void bind(JXTreeTable treeTable) {
  1308.             // Suppress potentially subversive invocation!
  1309.             // Prevent clearing out the deck for possible hijack attempt later!
  1310.             if (treeTable == null) {
  1311.                 throw new IllegalArgumentException("null treeTable");
  1312.             }
  1313.             if (this.treeTable == null) {
  1314.                 this.treeTable = treeTable;
  1315.             }
  1316.             else {
  1317.                 throw new IllegalArgumentException("renderer already bound");
  1318.             }
  1319.         }
  1320.         /**
  1321.          * updateUI is overridden to set the colors of the Tree's renderer
  1322.          * to match that of the table.
  1323.          */
  1324.         public void updateUI() {
  1325.             super.updateUI();
  1326.             // Make the tree's cell renderer use the table's cell selection
  1327.             // colors.
  1328.             TreeCellRenderer tcr = getCellRenderer();
  1329.             if (tcr instanceof DefaultTreeCellRenderer) {
  1330.                 DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr);
  1331.                 // For 1.1 uncomment this, 1.2 has a bug that will cause an
  1332.                 // exception to be thrown if the border selection color is null.
  1333.                 dtcr.setBorderSelectionColor(null);
  1334.                 dtcr.setTextSelectionColor(
  1335.                     UIManager.getColor("Table.selectionForeground"));
  1336.                 dtcr.setBackgroundSelectionColor(
  1337.                     UIManager.getColor("Table.selectionBackground"));
  1338.             }
  1339.         }
  1340.         /**
  1341.          * Sets the row height of the tree, and forwards the row height to
  1342.          * the table.
  1343.          */
  1344.         public void setRowHeight(int rowHeight) {
  1345.             super.setRowHeight(rowHeight);
  1346.             if (rowHeight > 0) {
  1347.                 // JW: setting the largeModel property is suggested in
  1348.                 // #25-swingx. 
  1349.                 // backing out: leads to NPEs and icons not showing
  1350. //                setLargeModel(true);
  1351.                 if (treeTable != null) {
  1352.                     // Reconcile semantic differences between JTable and JTree
  1353.                     final int tableRowMargin = treeTable.getRowMargin();
  1354.                     assert tableRowMargin >= 0;
  1355.                     final int tableRowHeight = rowHeight - (tableRowMargin << 1);
  1356.                     if (treeTable.getRowHeight() != tableRowHeight) {
  1357.                         treeTable.adminSetRowHeight(tableRowHeight);
  1358.                     }
  1359.                 }
  1360.             } 
  1361. //            else {
  1362. //                setLargeModel(false);
  1363. //            }
  1364.         }
  1365.         /**
  1366.          * This is overridden to set the height to match that of the JTable.
  1367.          */
  1368.         public void setBounds(int x, int y, int w, int h) {
  1369.             if (treeTable != null) {
  1370.                 y = 0;
  1371.                 // It is not enough to set the height to treeTable.getHeight()
  1372.                 h = treeTable.getRowCount() * this.getRowHeight();
  1373.             }
  1374.             super.setBounds(x, y, w, h);
  1375.         }
  1376.         /**
  1377.          * Sublcassed to translate the graphics such that the last visible row
  1378.          * will be drawn at 0,0.
  1379.          */
  1380.         public void paint(Graphics g) {
  1381.             int rowMargin = treeTable.getRowMargin();
  1382.             // MUST account for rowMargin for precise positioning.
  1383.             // Offset by (rowMargin * 3)/2, and remember to offset by an
  1384.             // additional pixel if rowMargin is odd!
  1385.             int margins = rowMargin + (rowMargin >> 1) + (rowMargin % 2);
  1386.             int translationOffset = margins + visibleRow * getRowHeight();
  1387.             g.translate(0, -translationOffset);
  1388.             hierarchicalColumnWidth = getWidth();
  1389.             super.paint(g);
  1390.             // Draw the Table border if we have focus.
  1391.             if (highlightBorder != null) {
  1392.                 // #170: border not drawn correctly
  1393.                 // JW: position the border to be drawn in translated area
  1394.                 // still not satifying in all cases...
  1395.                 // RG: Now it satisfies (at least for the row margins)
  1396.                 // Still need to make similar adjustments for column margins...
  1397.                 highlightBorder.paintBorder(this, g, 0, translationOffset,
  1398.                         getWidth(),
  1399.                         // uhhh getRowHeight() + 1 - 2 * (margins)
  1400.                         getRowHeight() - (rowMargin << 1) - rowMargin); // RG:
  1401.                                                                         // subtract
  1402.                                                                         // (rowMargin
  1403.                                                                         // * 3)
  1404.             }
  1405.         }
  1406.         public Component getTableCellRendererComponent(JTable table,
  1407.             Object value,
  1408.             boolean isSelected, boolean hasFocus, int row, int column) {
  1409.             assert table == treeTable;
  1410.             if (isSelected) {
  1411.                 setBackground(table.getSelectionBackground());
  1412.                 setForeground(table.getSelectionForeground());
  1413.             }
  1414.             else {
  1415.                 setBackground(table.getBackground());
  1416.                setForeground(table.getForeground());
  1417.             }
  1418.             highlightBorder = null;
  1419.             if (treeTable != null) {
  1420.                 if (treeTable.realEditingRow() == row &&
  1421.                     treeTable.getEditingColumn() == column) {
  1422.                 }
  1423.                 else if (hasFocus) {
  1424.                     highlightBorder = UIManager.getBorder(
  1425.                         "Table.focusCellHighlightBorder");
  1426.                 }
  1427.             }
  1428.             
  1429.             visibleRow = row;
  1430.             return this;
  1431.         }
  1432.         private class ClippedTreeCellRenderer extends DefaultTreeCellRenderer {
  1433.             public void paint(Graphics g) {
  1434.                 String fullText = super.getText();
  1435.                 // getText() calls tree.convertValueToText();
  1436.                 // tree.convertValueToText() should call treeModel.convertValueToText(), if possible
  1437.                 String shortText = SwingUtilities.layoutCompoundLabel(
  1438.                     this, g.getFontMetrics(), fullText, getIcon(),
  1439.                     getVerticalAlignment(), getHorizontalAlignment(),
  1440.                     getVerticalTextPosition(), getHorizontalTextPosition(),
  1441.                     getItemRect(itemRect), iconRect, textRect,
  1442.                     getIconTextGap());
  1443.                 /** @todo setText is more heavyweight than we want in this
  1444.                  * situation. Make JLabel.text protected instead of private.
  1445.                  */
  1446.                 setText(shortText); // temporarily truncate text
  1447.                 super.paint(g);
  1448.                 setText(fullText); // restore full text
  1449.             }
  1450.             private Rectangle getItemRect(Rectangle itemRect) {
  1451.                 getBounds(itemRect);
  1452.                 itemRect.width = hierarchicalColumnWidth - itemRect.x;
  1453.                 return itemRect;
  1454.             }
  1455.             // Rectangles filled in by SwingUtilities.layoutCompoundLabel();
  1456.             private final Rectangle iconRect = new Rectangle();
  1457.             private final Rectangle textRect = new Rectangle();
  1458.             // Rectangle filled in by this.getItemRect();
  1459.             private final Rectangle itemRect = new Rectangle();
  1460.         }
  1461.         /** Border to draw around the tree, if this is non-null, it will
  1462.          * be painted. */
  1463.         protected Border highlightBorder = null;
  1464.         protected JXTreeTable treeTable = null;
  1465.         protected int visibleRow = 0;
  1466.         // A JXTreeTable may not have more than one hierarchical column
  1467.         private int hierarchicalColumnWidth = 0;
  1468.     }
  1469.     /**
  1470.      * Returns the adapter that knows how to access the component data model.
  1471.      * The component data adapter is used by filters, sorters, and highlighters.
  1472.      *
  1473.      * @return the adapter that knows how to access the component data model
  1474.      */
  1475.     protected ComponentAdapter getComponentAdapter() {
  1476.         if (dataAdapter == null) {
  1477.             dataAdapter = new TreeTableDataAdapter(this); 
  1478.         }
  1479.         // MUST ALWAYS ACCESS dataAdapter through accessor method!!!
  1480.         return dataAdapter;
  1481.     }
  1482.     private final static Dimension spacing = new Dimension(0, 2);
  1483.     protected static class TreeTableDataAdapter extends JXTable.TableAdapter {
  1484.         private final JXTreeTable table;
  1485.         /**
  1486.          * Constructs a <code>TreeTableDataAdapter</code> for the specified
  1487.          * target component.
  1488.          *
  1489.          * @param component the target component
  1490.          */
  1491.         public TreeTableDataAdapter(JXTreeTable component) {
  1492.             super(component);
  1493.             table = component;
  1494.         }
  1495.         public JXTreeTable getTreeTable() {
  1496.             return table;
  1497.         }
  1498.         public boolean isExpanded() {
  1499.             return super.isExpanded(); /** @todo implement this method */
  1500.         }
  1501.         public boolean hasFocus() {
  1502.             boolean focus = super.hasFocus(); /** @todo implement this method */
  1503.             return focus;
  1504.         }
  1505.         public boolean isLeaf() {
  1506.             return super.isLeaf(); /** @todo implement this method */
  1507.         }
  1508.         /**
  1509.          *
  1510.          * @return true if the cell identified by this adapter displays hierarchical
  1511.          *      nodes; false otherwise
  1512.          */
  1513.         public boolean isHierarchical() {
  1514.             return table.isHierarchical(column);
  1515.         }
  1516.     }
  1517. }