001 /*
002 * Copyright (c) 1995-2010, The University of Sheffield. See the file
003 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004 *
005 * This file is part of GATE (see http://gate.ac.uk/), and is free
006 * software, licenced under the GNU Library General Public License,
007 * Version 2, June 1991 (in the distribution as file licence.html,
008 * and also available at http://gate.ac.uk/gate/licence.html).
009 *
010 * Valentin Tablan 06/03/2001
011 *
012 * $Id: JTreeTable.java 12006 2009-12-01 17:24:28Z thomas_heitz $
013 *
014 */
015 package gate.swing;
016
017 import java.awt.*;
018 import java.awt.event.MouseAdapter;
019 import java.awt.event.MouseEvent;
020 import java.beans.PropertyChangeEvent;
021 import java.beans.PropertyChangeListener;
022
023 import javax.swing.*;
024 import javax.swing.event.*;
025 import javax.swing.table.*;
026 import javax.swing.tree.*;
027
028
029 /**
030 * A TreeTable component. That is a component that looks like a table apart
031 * from the first column that contains a tree.
032 */
033 public class JTreeTable extends XJTable {
034
035 /**The tree used to render the first column*/
036 protected CustomJTree tree;
037
038 /**The model for this component*/
039 protected TreeTableModel treeTableModel;
040
041 /**
042 * The adapter used internally to convert a tree model into a table model.
043 */
044 protected TreeTableModelAdapter modelAdapter;
045
046 /**
047 * Constructs a JTreeTable from a model
048 */
049 public JTreeTable(TreeTableModel model) {
050 super();
051 this.treeTableModel = model;
052
053 initLocalData();
054 initGuiComponents();
055 initListeners();
056
057 super.setSortable(false);
058 }
059
060 protected void initLocalData(){
061 }
062
063 protected void initGuiComponents(){
064 // Create the tree. It will be used by the table renderer to draw the cells
065 //in the first column
066 tree = new CustomJTree();
067 tree.setModel(treeTableModel);
068 tree.setEditable(false);
069
070 // Install a tableModel representing the visible rows in the tree.
071 modelAdapter = new TreeTableModelAdapter(treeTableModel);
072 super.setModel(modelAdapter);
073
074 // Force the JTable and JTree to share their row selection models.
075 tree.setSelectionModel(new DefaultTreeSelectionModel() {
076 //extend the constructor
077 {
078 setSelectionModel(listSelectionModel);
079 }
080 });
081
082 setAutoCreateColumnsFromModel(false);
083 //Install the renderer and editor
084 getColumnModel().getColumn(0).setCellRenderer(new TreeTableCellRenderer());
085 getColumnModel().getColumn(0).setCellEditor(new TreeTableCellEditor());
086
087 setShowGrid(false);
088
089 setRowMargin(0);
090 }
091
092 protected void initListeners(){
093 //install the mouse listener that will forward the mouse events to the tree
094 addMouseListener(new MouseHandler());
095
096 getColumnModel().getColumn(0).addPropertyChangeListener(new PropertyChangeListener() {
097 public void propertyChange(PropertyChangeEvent e) {
098 if(e.getPropertyName().equals("width")){
099 int width = ((Number)e.getNewValue()).intValue();
100 int height = tree.getSize().height;
101 tree.setSize(width, height);
102 }
103 }
104 });
105 }
106
107 /**
108 * Overrides the setSortable() method from {@link XJTable} so the table is NOT
109 * sortable. In a tree-table component the ordering for the rows is given by
110 * the structure of the tree and they cannot be reordered.
111 */
112 public void setSortable(boolean b){
113 throw new UnsupportedOperationException(
114 "A JTreeTable component cannot be sortable!\n" +
115 "The rows order is defined by the tree structure.");
116 }
117
118 public JTree getTree(){
119 return tree;
120 }
121
122 public void expandPath(TreePath path){
123 tree.expandPath(path);
124 }
125
126 public void expandRow(int row){
127 tree.expandRow(row);
128 }
129
130 /**
131 * The renderer used to display the table cells containing tree nodes.
132 * Will use an internal JTree object to paint the nodes.
133 */
134 public class TreeTableCellRenderer extends DefaultTableCellRenderer {
135 public Component getTableCellRendererComponent(JTable table,
136 Object value,
137 boolean isSelected,
138 boolean hasFocus,
139 int row, int column) {
140 tree.setVisibleRow(row);
141 return tree;
142 }
143 }//public class TreeTableCellRenderer extends DefaultTableCellRenderer
144
145 /**
146 * The editor used to edit the nodes in the tree. It only forwards the
147 * requests to the tree's editor.
148 */
149 class TreeTableCellEditor extends DefaultCellEditor
150 implements TableCellEditor {
151 TreeTableCellEditor(){
152 super(new JTextField());
153 //placeHolder = new PlaceHolder();
154 editor = tree.getCellEditor();
155 setClickCountToStart(0);
156 }
157
158 public Component getTableCellEditorComponent(JTable table,
159 Object value,
160 boolean isSelected,
161 int row,
162 int column) {
163
164 editor = tree.getCellEditor();
165
166 editor.addCellEditorListener(new CellEditorListener() {
167 public void editingStopped(ChangeEvent e) {
168 fireEditingStopped();
169 }
170
171 public void editingCanceled(ChangeEvent e) {
172 fireEditingCanceled();
173 }
174 });
175
176 editorComponent = editor.getTreeCellEditorComponent(
177 tree, tree.getPathForRow(row).getLastPathComponent(),
178 isSelected, tree.isExpanded(row),
179 tree.getModel().isLeaf(
180 tree.getPathForRow(row).getLastPathComponent()
181 ),
182 row);
183 Box box = Box.createHorizontalBox();
184 box.add(Box.createHorizontalStrut(tree.getRowBounds(row).x));
185 box.add(editorComponent);
186 return box;
187 // return editorComponent;
188 }
189
190 public Object getCellEditorValue() {
191 return editor == null ? null : editor.getCellEditorValue();
192 }
193
194 public boolean stopCellEditing(){
195 return editor == null ? true : editor.stopCellEditing();
196 }
197
198 public void cancelCellEditing(){
199 if(editor != null) editor.cancelCellEditing();
200 }
201
202 TreeCellEditor editor;
203 Component editorComponent;
204 }
205
206 /**
207 * Class used to convert the mouse events from the JTreeTable component space
208 * into the JTree space. It is used to forward the mouse events to the tree
209 * if they occured in the space used by the tree.
210 */
211 class MouseHandler extends MouseAdapter {
212 public void mousePressed(MouseEvent e) {
213 if(columnAtPoint(e.getPoint()) == 0){
214 tree.dispatchEvent(convertEvent(e));
215 }
216 }
217
218 public void mouseReleased(MouseEvent e) {
219 if(columnAtPoint(e.getPoint()) == 0){
220 tree.dispatchEvent(convertEvent(e));
221 }
222 }
223
224 public void mouseClicked(MouseEvent e) {
225 if(columnAtPoint(e.getPoint()) == 0){
226 tree.dispatchEvent(convertEvent(e));
227 }
228 }
229
230
231 public void mouseEntered(MouseEvent e) {
232 if(columnAtPoint(e.getPoint()) == 0){
233 tree.dispatchEvent(convertEvent(e));
234 }
235 }
236
237 public void mouseExited(MouseEvent e) {
238 if(columnAtPoint(e.getPoint()) == 0){
239 tree.dispatchEvent(convertEvent(e));
240 }
241 }
242
243 protected MouseEvent convertEvent(MouseEvent e){
244 int column = 0;
245 int row = rowAtPoint(e.getPoint());
246
247 //move the event from table to tree coordinates
248 Rectangle tableCellRect = getCellRect(row, column, false);
249 Rectangle treeCellRect = tree.getRowBounds(row);
250 int dx = 0;
251 if(tableCellRect != null) dx = -tableCellRect.x;
252 int dy = 0;
253 if(tableCellRect !=null && treeCellRect != null)
254 dy = treeCellRect.y -tableCellRect.y;
255 e.translatePoint(dx, dy);
256
257
258 return new MouseEvent(
259 tree, e.getID(), e.getWhen(), e.getModifiers(),
260 e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
261 );
262 }
263 }
264
265 /**
266 * A wrapper that reads a TreeTableModel and behaves as a TableModel
267 */
268 class TreeTableModelAdapter extends AbstractTableModel{
269 public TreeTableModelAdapter(TreeTableModel treeTableModel) {
270 tree.addTreeExpansionListener(new TreeExpansionListener() {
271 // Don't use fireTableRowsInserted() here;
272 // the selection model would get updated twice.
273 public void treeExpanded(TreeExpansionEvent event) {
274 fireTableDataChanged();
275 }
276 public void treeCollapsed(TreeExpansionEvent event) {
277 fireTableDataChanged();
278 }
279 });
280 tree.getModel().addTreeModelListener(new TreeModelListener() {
281 public void treeNodesChanged(TreeModelEvent e) {
282 fireTableDataChanged();
283 }
284 public void treeNodesInserted(TreeModelEvent e) {
285 fireTableDataChanged();
286 }
287 public void treeNodesRemoved(TreeModelEvent e) {
288 fireTableDataChanged();
289 }
290 public void treeStructureChanged(TreeModelEvent e) {
291 fireTableDataChanged();
292 }
293 });
294 }
295
296 // Wrappers, implementing TableModel interface.
297 public int getColumnCount() {
298 return treeTableModel.getColumnCount();
299 }
300
301 public String getColumnName(int column) {
302 return treeTableModel.getColumnName(column);
303 }
304
305 public Class getColumnClass(int column) {
306 if(column == 0) return TreeTableModel.class;
307 else return treeTableModel.getColumnClass(column);
308 }
309
310 public int getRowCount() {
311 return tree.getRowCount();
312 }
313
314 protected Object nodeForRow(int row) {
315 TreePath treePath = tree.getPathForRow(row);
316 return treePath.getLastPathComponent();
317 }
318
319 public Object getValueAt(int row, int column) {
320 if(column == 0) return treeTableModel;
321 else return treeTableModel.getValueAt(nodeForRow(row), column);
322 }
323
324 public boolean isCellEditable(int row, int column) {
325 return treeTableModel.isCellEditable(nodeForRow(row), column);
326 }
327
328 public void setValueAt(Object value, int row, int column) {
329 Object node = nodeForRow(row);
330 treeTableModel.setValueAt(value, node, column);
331 }
332 }//class TreeTableModelAdapter extends AbstractTableModel
333
334 /**
335 * The JTree used for rendering the first column.
336 */
337 class CustomJTree extends JTree {
338
339 public void updateUI(){
340 super.updateUI();
341 setRowHeight(0);
342 }
343
344
345 public void setVisibleRow(int row){
346 visibleRow = row;
347 }
348
349 /**
350 * Paints only the current cell in the table
351 */
352 public void paint(Graphics g){
353 Rectangle rowBounds = getRowBounds(visibleRow);
354 g.translate(0, -rowBounds.y);
355 Rectangle oldClip = g.getClipBounds();
356 // Rectangle newClip = oldClip.intersection(
357 // new Rectangle(oldClip.x, rowBounds.y, oldClip.width,
358 // rowBounds.height));
359 // g.setClip(newClip);
360 //re-implemented more efficiently below:
361 int newY = Math.max(oldClip.y, rowBounds.y);
362 int newHeight = Math.min(rowBounds.height - (rowBounds.y - newY),
363 oldClip.height);
364 g.setClip(oldClip.x, newY, oldClip.width, newHeight);
365 super.paint(g);
366 }
367
368
369 public Dimension getPreferredSize(){
370 return new Dimension(super.getPreferredSize().width,
371 getRowBounds(visibleRow).height);
372 }
373
374
375 public void validate(){}
376 public void revalidate(){}
377 public void repaint(long tm, int x, int y, int width, int height){}
378 public void repaint(Rectangle r){}
379
380 protected int visibleRow;
381
382 /* (non-Javadoc)
383 * @see javax.swing.JTree#setRootVisible(boolean)
384 */
385 @Override
386 public void setRootVisible(boolean rootVisible) {
387 boolean oldValue = isRootVisible();
388 if(oldValue != rootVisible){
389 super.setRootVisible(rootVisible);
390 modelAdapter.fireTableDataChanged();
391 }
392 }
393 }
394
395 /*
396 class SmartTreeCellRenderer implements TreeCellRenderer{
397
398 SmartTreeCellRenderer(TreeCellRenderer renderer){
399 originalRenderer = renderer;
400 }
401
402 public Component getTreeCellRendererComponent(JTree tree,
403 Object value,
404 boolean selected,
405 boolean expanded,
406 boolean leaf,
407 int row,
408 boolean hasFocus){
409 Component comp = originalRenderer.getTreeCellRendererComponent(
410 tree, value, selected, expanded, leaf, row, hasFocus);
411 if(comp instanceof JComponent &&
412 comp.getPreferredSize().height < getRowHeight(row)){
413 ((JComponent)comp).setPreferredSize(
414 new Dimension(comp.getPreferredSize().width,
415 getRowHeight(row))
416 );
417 }
418 return comp;
419 }
420
421 public TreeCellRenderer getOriginalRenderer(){
422 return originalRenderer;
423 }
424
425 TreeCellRenderer originalRenderer;
426 }
427 */
428 }//public class JTreeTable extends XJTable
|