| JTreeTable |

    1   /*
    2    *  Copyright (c) 1998-2001, The University of Sheffield.
    3    *
    4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
    5    *  software, licenced under the GNU Library General Public License,
    6    *  Version 2, June 1991 (in the distribution as file licence.html,
    7    *  and also available at http://gate.ac.uk/gate/licence.html).
    8    *
    9    *  Valentin Tablan 06/03/2001
    10   *
    11   *  $Id: JTreeTable.java,v 1.7 2001/07/16 13:20:57 valyt Exp $
    12   *
    13   */
    14  package gate.swing;
    15
    16  import javax.swing.*;
    17  import javax.swing.event.*;
    18  import javax.swing.tree.*;
    19  import javax.swing.table.*;
    20  import javax.swing.event.TreeExpansionEvent;
    21  import javax.swing.event.TreeExpansionListener;
    22
    23
    24  import java.awt.Dimension;
    25  import java.awt.Component;
    26  import java.awt.Graphics;
    27  import java.awt.Rectangle;
    28  import java.awt.Color;
    29  import java.awt.image.BufferedImage;
    30  import java.awt.event.*;
    31  import java.util.EventObject;
    32
    33  import java.beans.*;
    34
    35
    36  /**
    37   * A TreeTable component. That is a component that looks like a table apart
    38   * from the first column that contains a tree.
    39   */
    40  public class JTreeTable extends XJTable {
    41
    42    /**The tree used to render the first column*/
    43    protected CustomJTree tree;
    44
    45    /**The model for this component*/
    46    protected TreeTableModel treeTableModel;
    47
    48    /**
    49     * Constructs a JTreeTable from a model
    50     */
    51    public JTreeTable(TreeTableModel model) {
    52      super();
    53      this.treeTableModel = model;
    54
    55      initLocalData();
    56      initGuiComponents();
    57      initListeners();
    58
    59      super.setSortable(false);
    60    }
    61
    62    protected void initLocalData(){
    63    }
    64
    65    protected void initGuiComponents(){
    66      // Create the tree. It will be used by the table renderer to draw the cells
    67      //in the first column
    68      tree = new CustomJTree();
    69      tree.setModel(treeTableModel);
    70      tree.setEditable(false);
    71
    72      // Install a tableModel representing the visible rows in the tree.
    73      super.setModel(new TreeTableModelAdapter(treeTableModel));
    74
    75      // Force the JTable and JTree to share their row selection models.
    76      tree.setSelectionModel(new DefaultTreeSelectionModel() {
    77        //extend the constructor
    78        {
    79          setSelectionModel(listSelectionModel);
    80        }
    81      });
    82
    83      setAutoCreateColumnsFromModel(false);
    84      //Install the renderer and editor
    85      getColumnModel().getColumn(0).setCellRenderer(new TreeTableCellRenderer());
    86      getColumnModel().getColumn(0).setCellEditor(new TreeTableCellEditor());
    87
    88      setShowGrid(false);
    89    }
    90
    91    protected void initListeners(){
    92      //install the mouse listener that will forward the mouse events to the tree
    93      addMouseListener(new MouseHandler());
    94
    95      getColumnModel().getColumn(0).addPropertyChangeListener(new PropertyChangeListener() {
    96        public void propertyChange(PropertyChangeEvent e) {
    97          if(e.getPropertyName().equals("width")){
    98            int width = ((Number)e.getNewValue()).intValue();
    99            int height = tree.getSize().height;
    100           tree.setSize(width, height);
    101         }
    102       }
    103     });
    104   }
    105
    106   /**
    107    * Overrides the setSortable() method from {@link XJTable} so the table is NOT
    108    * sortable. In a tree-table component the ordering for the rows is given by
    109    * the structure of the tree and they cannot be reordered.
    110    */
    111   public void setSortable(boolean b){
    112     throw new UnsupportedOperationException(
    113           "A JTreeTable component cannot be sortable!\n" +
    114           "The rows order is defined by the tree structure.");
    115   }
    116
    117   public JTree getTree(){
    118     return tree;
    119   }
    120
    121   public void expandPath(TreePath path){
    122     tree.expandPath(path);
    123   }
    124
    125   public void expandRow(int row){
    126     tree.expandRow(row);
    127   }
    128
    129   /**
    130    * The renderer used to display the table cells containing tree nodes.
    131    * Will use an internal JTree object to paint the nodes.
    132    */
    133   public class TreeTableCellRenderer extends DefaultTableCellRenderer {
    134     public Component getTableCellRendererComponent(JTable table,
    135                      Object value,
    136                      boolean isSelected,
    137                      boolean hasFocus,
    138                      int row, int column) {
    139       tree.setVisibleRow(row);
    140       return tree;
    141     }
    142   }//public class TreeTableCellRenderer extends DefaultTableCellRenderer
    143
    144   /**
    145    * The editor used to edit the nodes in the tree. It only forwards the
    146    * requests to the tree's editor.
    147    */
    148   class TreeTableCellEditor extends DefaultCellEditor
    149                             implements TableCellEditor {
    150     TreeTableCellEditor(){
    151       super(new JTextField());
    152       //placeHolder = new PlaceHolder();
    153       editor = tree.getCellEditor();
    154       setClickCountToStart(0);
    155     }
    156
    157     public Component getTableCellEditorComponent(JTable table,
    158                                                  Object value,
    159                                                  boolean isSelected,
    160                                                  int row,
    161                                                  int column) {
    162
    163       editor = tree.getCellEditor();
    164
    165       editor.addCellEditorListener(new CellEditorListener() {
    166         public void editingStopped(ChangeEvent e) {
    167           fireEditingStopped();
    168         }
    169
    170         public void editingCanceled(ChangeEvent e) {
    171           fireEditingCanceled();
    172         }
    173       });
    174
    175       editorComponent = editor.getTreeCellEditorComponent(
    176                     tree, tree.getPathForRow(row).getLastPathComponent(),
    177                     isSelected, tree.isExpanded(row),
    178                     tree.getModel().isLeaf(
    179                       tree.getPathForRow(row).getLastPathComponent()
    180                     ),
    181                     row);
    182       Box box = Box.createHorizontalBox();
    183       box.add(Box.createHorizontalStrut(tree.getRowBounds(row).x));
    184       box.add(editorComponent);
    185       return box;
    186 //      return editorComponent;
    187     }
    188
    189     public Object getCellEditorValue() {
    190       return editor == null ? null : editor.getCellEditorValue();
    191     }
    192
    193     public boolean stopCellEditing(){
    194       return editor == null ? true : editor.stopCellEditing();
    195     }
    196
    197     public void cancelCellEditing(){
    198       if(editor != null) editor.cancelCellEditing();
    199     }
    200
    201     TreeCellEditor editor;
    202     Component editorComponent;
    203   }
    204
    205   /**
    206    * Class used to convert the mouse events from the JTreeTable component space
    207    * into the JTree space. It is used to forward the mouse events to the tree
    208    * if they occured in the space used by the tree.
    209    */
    210   class MouseHandler extends MouseAdapter {
    211     public void mousePressed(MouseEvent e) {
    212       if(columnAtPoint(e.getPoint()) == 0){
    213         tree.dispatchEvent(convertEvent(e));
    214       }
    215     }
    216
    217     public void mouseReleased(MouseEvent e) {
    218       if(columnAtPoint(e.getPoint()) == 0){
    219         tree.dispatchEvent(convertEvent(e));
    220       }
    221     }
    222
    223     public void mouseClicked(MouseEvent e) {
    224       if(columnAtPoint(e.getPoint()) == 0){
    225         tree.dispatchEvent(convertEvent(e));
    226       }
    227     }
    228
    229
    230     public void mouseEntered(MouseEvent e) {
    231       if(columnAtPoint(e.getPoint()) == 0){
    232         tree.dispatchEvent(convertEvent(e));
    233       }
    234     }
    235
    236     public void mouseExited(MouseEvent e) {
    237       if(columnAtPoint(e.getPoint()) == 0){
    238         tree.dispatchEvent(convertEvent(e));
    239       }
    240     }
    241
    242     protected MouseEvent convertEvent(MouseEvent e){
    243       int column = 0;
    244       int row = rowAtPoint(e.getPoint());
    245
    246       //move the event from table to tree coordinates
    247       Rectangle tableCellRect = getCellRect(row, column, false);
    248       Rectangle treeCellRect = tree.getRowBounds(row);
    249       int dx = 0;
    250       if(tableCellRect != null) dx = -tableCellRect.x;
    251       int dy = 0;
    252       if(tableCellRect !=null && treeCellRect != null)
    253         dy = treeCellRect.y -tableCellRect.y;
    254       e.translatePoint(dx, dy);
    255
    256
    257       return new MouseEvent(
    258         tree, e.getID(), e.getWhen(), e.getModifiers(),
    259         e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
    260       );
    261     }
    262   }
    263
    264   /**
    265    * A wrapper that reads a TreeTableModel and behaves as a TableModel
    266    */
    267   class TreeTableModelAdapter extends AbstractTableModel{
    268     public TreeTableModelAdapter(TreeTableModel treeTableModel) {
    269       tree.addTreeExpansionListener(new TreeExpansionListener() {
    270         // Don't use fireTableRowsInserted() here;
    271         // the selection model would get  updated twice.
    272         public void treeExpanded(TreeExpansionEvent event) {
    273           fireTableDataChanged();
    274         }
    275         public void treeCollapsed(TreeExpansionEvent event) {
    276           fireTableDataChanged();
    277         }
    278       });
    279       tree.getModel().addTreeModelListener(new TreeModelListener() {
    280         public void treeNodesChanged(TreeModelEvent e) {
    281           fireTableDataChanged();
    282         }
    283         public void treeNodesInserted(TreeModelEvent e) {
    284           fireTableDataChanged();
    285         }
    286         public void treeNodesRemoved(TreeModelEvent e) {
    287           fireTableDataChanged();
    288         }
    289         public void treeStructureChanged(TreeModelEvent e) {
    290           fireTableDataChanged();
    291         }
    292       });
    293     }
    294
    295
    296
    297     // Wrappers, implementing TableModel interface.
    298     public int getColumnCount() {
    299       return treeTableModel.getColumnCount();
    300     }
    301
    302     public String getColumnName(int column) {
    303       return treeTableModel.getColumnName(column);
    304     }
    305
    306     public Class getColumnClass(int column) {
    307       if(column == 0) return TreeTableModel.class;
    308       else return treeTableModel.getColumnClass(column);
    309     }
    310
    311     public int getRowCount() {
    312       return tree.getRowCount();
    313     }
    314
    315     protected Object nodeForRow(int row) {
    316       TreePath treePath = tree.getPathForRow(row);
    317       return treePath.getLastPathComponent();
    318     }
    319
    320     public Object getValueAt(int row, int column) {
    321       if(column == 0) return treeTableModel;
    322       else return treeTableModel.getValueAt(nodeForRow(row), column);
    323     }
    324
    325     public boolean isCellEditable(int row, int column) {
    326       return treeTableModel.isCellEditable(nodeForRow(row), column);
    327     }
    328
    329     public void setValueAt(Object value, int row, int column) {
    330       Object node = nodeForRow(row);
    331       treeTableModel.setValueAt(value, node, column);
    332     }
    333   }//class TreeTableModelAdapter extends AbstractTableModel
    334
    335   /**
    336    * The JTree used for rendering the first column.
    337    */
    338   class CustomJTree extends JTree {
    339
    340     public void updateUI(){
    341       super.updateUI();
    342       setRowHeight(0);
    343     }
    344
    345
    346     public void setVisibleRow(int row){
    347       visibleRow = row;
    348     }
    349
    350     /**
    351      * Paints only the current cell in the table
    352      */
    353     public void paint(Graphics g){
    354       Rectangle rect = getRowBounds(visibleRow);
    355       Rectangle bounds = g.getClipBounds();
    356       g.translate(0, -rect.y);
    357       g.setClip(bounds.x, rect.y, bounds.width, rect.height);
    358       super.paint(g);
    359     }
    360
    361
    362     public Dimension getPreferredSize(){
    363       return new Dimension(super.getPreferredSize().width,
    364                            getRowBounds(visibleRow).height);
    365     }
    366
    367
    368     public void validate(){}
    369     public void revalidate(){}
    370     public void repaint(long tm, int x, int y, int width, int height){}
    371     public void repaint(Rectangle r){}
    372
    373     protected int visibleRow;
    374   }
    375
    376 /*
    377   class SmartTreeCellRenderer implements TreeCellRenderer{
    378
    379     SmartTreeCellRenderer(TreeCellRenderer renderer){
    380       originalRenderer = renderer;
    381     }
    382
    383     public Component getTreeCellRendererComponent(JTree tree,
    384                                               Object value,
    385                                               boolean selected,
    386                                               boolean expanded,
    387                                               boolean leaf,
    388                                               int row,
    389                                               boolean hasFocus){
    390       Component comp = originalRenderer.getTreeCellRendererComponent(
    391                        tree, value, selected, expanded, leaf, row, hasFocus);
    392       if(comp instanceof JComponent &&
    393          comp.getPreferredSize().height < getRowHeight(row)){
    394         ((JComponent)comp).setPreferredSize(
    395             new Dimension(comp.getPreferredSize().width,
    396             getRowHeight(row))
    397         );
    398       }
    399       return comp;
    400     }
    401
    402     public TreeCellRenderer getOriginalRenderer(){
    403       return originalRenderer;
    404     }
    405
    406     TreeCellRenderer originalRenderer;
    407   }
    408 */
    409 }//public class JTreeTable extends XJTable
    410

| JTreeTable |