1   
14  package gate.swing;
15  
16  import java.awt.*;
17  import java.awt.event.MouseAdapter;
18  import java.awt.event.MouseEvent;
19  import java.beans.PropertyChangeEvent;
20  import java.beans.PropertyChangeListener;
21  
22  import javax.swing.*;
23  import javax.swing.event.*;
24  import javax.swing.table.*;
25  import javax.swing.tree.*;
26  
27  
28  
32  public class JTreeTable extends XJTable {
33  
34    
35    protected CustomJTree tree;
36  
37    
38    protected TreeTableModel treeTableModel;
39  
40    
43    public JTreeTable(TreeTableModel model) {
44      super();
45      this.treeTableModel = model;
46  
47      initLocalData();
48      initGuiComponents();
49      initListeners();
50  
51      super.setSortable(false);
52    }
53  
54    protected void initLocalData(){
55    }
56  
57    protected void initGuiComponents(){
58              tree = new CustomJTree();
61      tree.setModel(treeTableModel);
62      tree.setEditable(false);
63  
64          super.setModel(new TreeTableModelAdapter(treeTableModel));
66  
67          tree.setSelectionModel(new DefaultTreeSelectionModel() {
69              {
71          setSelectionModel(listSelectionModel);
72        }
73      });
74  
75      setAutoCreateColumnsFromModel(false);
76          getColumnModel().getColumn(0).setCellRenderer(new TreeTableCellRenderer());
78      getColumnModel().getColumn(0).setCellEditor(new TreeTableCellEditor());
79  
80      setShowGrid(false);
81    }
82  
83    protected void initListeners(){
84          addMouseListener(new MouseHandler());
86  
87      getColumnModel().getColumn(0).addPropertyChangeListener(new PropertyChangeListener() {
88        public void propertyChange(PropertyChangeEvent e) {
89          if(e.getPropertyName().equals("width")){
90            int width = ((Number)e.getNewValue()).intValue();
91            int height = tree.getSize().height;
92            tree.setSize(width, height);
93          }
94        }
95      });
96    }
97  
98    
103   public void setSortable(boolean b){
104     throw new UnsupportedOperationException(
105           "A JTreeTable component cannot be sortable!\n" +
106           "The rows order is defined by the tree structure.");
107   }
108 
109   public JTree getTree(){
110     return tree;
111   }
112 
113   public void expandPath(TreePath path){
114     tree.expandPath(path);
115   }
116 
117   public void expandRow(int row){
118     tree.expandRow(row);
119   }
120 
121   
125   public class TreeTableCellRenderer extends DefaultTableCellRenderer {
126     public Component getTableCellRendererComponent(JTable table,
127                      Object value,
128                      boolean isSelected,
129                      boolean hasFocus,
130                      int row, int column) {
131       tree.setVisibleRow(row);
132       return tree;
133     }
134   }
136   
140   class TreeTableCellEditor extends DefaultCellEditor
141                             implements TableCellEditor {
142     TreeTableCellEditor(){
143       super(new JTextField());
144             editor = tree.getCellEditor();
146       setClickCountToStart(0);
147     }
148 
149     public Component getTableCellEditorComponent(JTable table,
150                                                  Object value,
151                                                  boolean isSelected,
152                                                  int row,
153                                                  int column) {
154 
155       editor = tree.getCellEditor();
156 
157       editor.addCellEditorListener(new CellEditorListener() {
158         public void editingStopped(ChangeEvent e) {
159           fireEditingStopped();
160         }
161 
162         public void editingCanceled(ChangeEvent e) {
163           fireEditingCanceled();
164         }
165       });
166 
167       editorComponent = editor.getTreeCellEditorComponent(
168                     tree, tree.getPathForRow(row).getLastPathComponent(),
169                     isSelected, tree.isExpanded(row),
170                     tree.getModel().isLeaf(
171                       tree.getPathForRow(row).getLastPathComponent()
172                     ),
173                     row);
174       Box box = Box.createHorizontalBox();
175       box.add(Box.createHorizontalStrut(tree.getRowBounds(row).x));
176       box.add(editorComponent);
177       return box;
178     }
180 
181     public Object getCellEditorValue() {
182       return editor == null ? null : editor.getCellEditorValue();
183     }
184 
185     public boolean stopCellEditing(){
186       return editor == null ? true : editor.stopCellEditing();
187     }
188 
189     public void cancelCellEditing(){
190       if(editor != null) editor.cancelCellEditing();
191     }
192 
193     TreeCellEditor editor;
194     Component editorComponent;
195   }
196 
197   
202   class MouseHandler extends MouseAdapter {
203     public void mousePressed(MouseEvent e) {
204       if(columnAtPoint(e.getPoint()) == 0){
205         tree.dispatchEvent(convertEvent(e));
206       }
207     }
208 
209     public void mouseReleased(MouseEvent e) {
210       if(columnAtPoint(e.getPoint()) == 0){
211         tree.dispatchEvent(convertEvent(e));
212       }
213     }
214 
215     public void mouseClicked(MouseEvent e) {
216       if(columnAtPoint(e.getPoint()) == 0){
217         tree.dispatchEvent(convertEvent(e));
218       }
219     }
220 
221 
222     public void mouseEntered(MouseEvent e) {
223       if(columnAtPoint(e.getPoint()) == 0){
224         tree.dispatchEvent(convertEvent(e));
225       }
226     }
227 
228     public void mouseExited(MouseEvent e) {
229       if(columnAtPoint(e.getPoint()) == 0){
230         tree.dispatchEvent(convertEvent(e));
231       }
232     }
233 
234     protected MouseEvent convertEvent(MouseEvent e){
235       int column = 0;
236       int row = rowAtPoint(e.getPoint());
237 
238             Rectangle tableCellRect = getCellRect(row, column, false);
240       Rectangle treeCellRect = tree.getRowBounds(row);
241       int dx = 0;
242       if(tableCellRect != null) dx = -tableCellRect.x;
243       int dy = 0;
244       if(tableCellRect !=null && treeCellRect != null)
245         dy = treeCellRect.y -tableCellRect.y;
246       e.translatePoint(dx, dy);
247 
248 
249       return new MouseEvent(
250         tree, e.getID(), e.getWhen(), e.getModifiers(),
251         e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
252       );
253     }
254   }
255 
256   
259   class TreeTableModelAdapter extends AbstractTableModel{
260     public TreeTableModelAdapter(TreeTableModel treeTableModel) {
261       tree.addTreeExpansionListener(new TreeExpansionListener() {
262                         public void treeExpanded(TreeExpansionEvent event) {
265           fireTableDataChanged();
266         }
267         public void treeCollapsed(TreeExpansionEvent event) {
268           fireTableDataChanged();
269         }
270       });
271       tree.getModel().addTreeModelListener(new TreeModelListener() {
272         public void treeNodesChanged(TreeModelEvent e) {
273           fireTableDataChanged();
274         }
275         public void treeNodesInserted(TreeModelEvent e) {
276           fireTableDataChanged();
277         }
278         public void treeNodesRemoved(TreeModelEvent e) {
279           fireTableDataChanged();
280         }
281         public void treeStructureChanged(TreeModelEvent e) {
282           fireTableDataChanged();
283         }
284       });
285     }
286 
287 
288 
289         public int getColumnCount() {
291       return treeTableModel.getColumnCount();
292     }
293 
294     public String getColumnName(int column) {
295       return treeTableModel.getColumnName(column);
296     }
297 
298     public Class getColumnClass(int column) {
299       if(column == 0) return TreeTableModel.class;
300       else return treeTableModel.getColumnClass(column);
301     }
302 
303     public int getRowCount() {
304       return tree.getRowCount();
305     }
306 
307     protected Object nodeForRow(int row) {
308       TreePath treePath = tree.getPathForRow(row);
309       return treePath.getLastPathComponent();
310     }
311 
312     public Object getValueAt(int row, int column) {
313       if(column == 0) return treeTableModel;
314       else return treeTableModel.getValueAt(nodeForRow(row), column);
315     }
316 
317     public boolean isCellEditable(int row, int column) {
318       return treeTableModel.isCellEditable(nodeForRow(row), column);
319     }
320 
321     public void setValueAt(Object value, int row, int column) {
322       Object node = nodeForRow(row);
323       treeTableModel.setValueAt(value, node, column);
324     }
325   }
327   
330   class CustomJTree extends JTree {
331 
332     public void updateUI(){
333       super.updateUI();
334       setRowHeight(0);
335     }
336 
337 
338     public void setVisibleRow(int row){
339       visibleRow = row;
340     }
341 
342     
345     public void paint(Graphics g){
346       Rectangle rect = getRowBounds(visibleRow);
347       Rectangle bounds = g.getClipBounds();
348       g.translate(0, -rect.y);
349       g.setClip(bounds.x, rect.y, bounds.width, rect.height);
350       super.paint(g);
351     }
352 
353 
354     public Dimension getPreferredSize(){
355       return new Dimension(super.getPreferredSize().width,
356                            getRowBounds(visibleRow).height);
357     }
358 
359 
360     public void validate(){}
361     public void revalidate(){}
362     public void repaint(long tm, int x, int y, int width, int height){}
363     public void repaint(Rectangle r){}
364 
365     protected int visibleRow;
366   }
367 
368 
401 }