gate.swing.JTreeTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gate-core Show documentation
Show all versions of gate-core Show documentation
GATE - general achitecture for text engineering - is open source
software capable of solving almost any text processing problem. This
artifact enables you to embed the core GATE Embedded with its essential
dependencies. You will able to use the GATE Embedded API and load and
store GATE XML documents. This artifact is the perfect dependency for
CREOLE plugins or for applications that need to customize the GATE
dependencies due to confict with their own dependencies or for lower
footprint.
The newest version!
/*
* Copyright (c) 1995-2012, The University of Sheffield. See the file
* COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
*
* This file is part of GATE (see http://gate.ac.uk/), and is free
* software, licenced under the GNU Library General Public License,
* Version 2, June 1991 (in the distribution as file licence.html,
* and also available at http://gate.ac.uk/gate/licence.html).
*
* Valentin Tablan 06/03/2001
*
* $Id: JTreeTable.java 19756 2016-11-19 01:55:44Z markagreenwood $
*
*/
package gate.swing;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.tree.*;
/**
* A TreeTable component. That is a component that looks like a table apart
* from the first column that contains a tree.
*/
@SuppressWarnings("serial")
public class JTreeTable extends XJTable {
/**The tree used to render the first column*/
protected CustomJTree tree;
/**The model for this component*/
protected TreeTableModel treeTableModel;
/**
* The adapter used internally to convert a tree model into a table model.
*/
protected TreeTableModelAdapter modelAdapter;
/**
* Constructs a JTreeTable from a model
*/
public JTreeTable(TreeTableModel model) {
super();
this.treeTableModel = model;
initLocalData();
initGuiComponents();
initListeners();
super.setSortable(false);
}
protected void initLocalData(){
}
protected void initGuiComponents(){
// Create the tree. It will be used by the table renderer to draw the cells
//in the first column
tree = new CustomJTree();
tree.setModel(treeTableModel);
tree.setEditable(false);
// Install a tableModel representing the visible rows in the tree.
modelAdapter = new TreeTableModelAdapter(treeTableModel);
super.setModel(modelAdapter);
// Force the JTable and JTree to share their row selection models.
tree.setSelectionModel(new DefaultTreeSelectionModel() {
//extend the constructor
{
setSelectionModel(listSelectionModel);
}
});
setAutoCreateColumnsFromModel(false);
//Install the renderer and editor
getColumnModel().getColumn(0).setCellRenderer(new TreeTableCellRenderer());
getColumnModel().getColumn(0).setCellEditor(new TreeTableCellEditor());
setShowGrid(false);
setRowMargin(0);
}
protected void initListeners(){
//install the mouse listener that will forward the mouse events to the tree
addMouseListener(new MouseHandler());
getColumnModel().getColumn(0).addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
if(e.getPropertyName().equals("width")){
int width = ((Number)e.getNewValue()).intValue();
int height = tree.getSize().height;
tree.setSize(width, height);
}
}
});
}
/**
* Overrides the setSortable() method from {@link XJTable} so the table is NOT
* sortable. In a tree-table component the ordering for the rows is given by
* the structure of the tree and they cannot be reordered.
*/
@Override
public void setSortable(boolean b){
throw new UnsupportedOperationException(
"A JTreeTable component cannot be sortable!\n" +
"The rows order is defined by the tree structure.");
}
public JTree getTree(){
return tree;
}
public void expandPath(TreePath path){
tree.expandPath(path);
}
public void expandRow(int row){
tree.expandRow(row);
}
/**
* The renderer used to display the table cells containing tree nodes.
* Will use an internal JTree object to paint the nodes.
*/
public class TreeTableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row, int column) {
tree.setVisibleRow(row);
return tree;
}
}//public class TreeTableCellRenderer extends DefaultTableCellRenderer
/**
* The editor used to edit the nodes in the tree. It only forwards the
* requests to the tree's editor.
*/
class TreeTableCellEditor extends DefaultCellEditor {
TreeTableCellEditor(){
super(new JTextField());
//placeHolder = new PlaceHolder();
editor = tree.getCellEditor();
setClickCountToStart(0);
}
@Override
public Component getTableCellEditorComponent(JTable table,
Object value,
boolean isSelected,
int row,
int column) {
editor = tree.getCellEditor();
editor.addCellEditorListener(new CellEditorListener() {
@Override
public void editingStopped(ChangeEvent e) {
fireEditingStopped();
}
@Override
public void editingCanceled(ChangeEvent e) {
fireEditingCanceled();
}
});
editorComponent = editor.getTreeCellEditorComponent(
tree, tree.getPathForRow(row).getLastPathComponent(),
isSelected, tree.isExpanded(row),
tree.getModel().isLeaf(
tree.getPathForRow(row).getLastPathComponent()
),
row);
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalStrut(tree.getRowBounds(row).x));
box.add(editorComponent);
return box;
// return editorComponent;
}
@Override
public Object getCellEditorValue() {
return editor == null ? null : editor.getCellEditorValue();
}
@Override
public boolean stopCellEditing(){
return editor == null ? true : editor.stopCellEditing();
}
@Override
public void cancelCellEditing(){
if(editor != null) editor.cancelCellEditing();
}
TreeCellEditor editor;
Component editorComponent;
}
/**
* Class used to convert the mouse events from the JTreeTable component space
* into the JTree space. It is used to forward the mouse events to the tree
* if they occured in the space used by the tree.
*/
class MouseHandler extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
if(columnAtPoint(e.getPoint()) == 0){
tree.dispatchEvent(convertEvent(e));
}
}
@Override
public void mouseReleased(MouseEvent e) {
if(columnAtPoint(e.getPoint()) == 0){
tree.dispatchEvent(convertEvent(e));
}
}
@Override
public void mouseClicked(MouseEvent e) {
if(columnAtPoint(e.getPoint()) == 0){
tree.dispatchEvent(convertEvent(e));
}
}
@Override
public void mouseEntered(MouseEvent e) {
if(columnAtPoint(e.getPoint()) == 0){
tree.dispatchEvent(convertEvent(e));
}
}
@Override
public void mouseExited(MouseEvent e) {
if(columnAtPoint(e.getPoint()) == 0){
tree.dispatchEvent(convertEvent(e));
}
}
protected MouseEvent convertEvent(MouseEvent e){
int column = 0;
int row = rowAtPoint(e.getPoint());
//move the event from table to tree coordinates
Rectangle tableCellRect = getCellRect(row, column, false);
Rectangle treeCellRect = tree.getRowBounds(row);
int dx = 0;
if(tableCellRect != null) dx = -tableCellRect.x;
int dy = 0;
if(tableCellRect !=null && treeCellRect != null)
dy = treeCellRect.y -tableCellRect.y;
e.translatePoint(dx, dy);
return new MouseEvent(
tree, e.getID(), e.getWhen(), e.getModifiers(),
e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
);
}
}
/**
* A wrapper that reads a TreeTableModel and behaves as a TableModel
*/
class TreeTableModelAdapter extends AbstractTableModel{
public TreeTableModelAdapter(TreeTableModel treeTableModel) {
tree.addTreeExpansionListener(new TreeExpansionListener() {
// Don't use fireTableRowsInserted() here;
// the selection model would get updated twice.
@Override
public void treeExpanded(TreeExpansionEvent event) {
fireTableDataChanged();
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
fireTableDataChanged();
}
});
tree.getModel().addTreeModelListener(new TreeModelListener() {
@Override
public void treeNodesChanged(TreeModelEvent e) {
fireTableDataChanged();
}
@Override
public void treeNodesInserted(TreeModelEvent e) {
fireTableDataChanged();
}
@Override
public void treeNodesRemoved(TreeModelEvent e) {
fireTableDataChanged();
}
@Override
public void treeStructureChanged(TreeModelEvent e) {
fireTableDataChanged();
}
});
}
// Wrappers, implementing TableModel interface.
@Override
public int getColumnCount() {
return treeTableModel.getColumnCount();
}
@Override
public String getColumnName(int column) {
return treeTableModel.getColumnName(column);
}
@Override
public Class> getColumnClass(int column) {
if(column == 0) return TreeTableModel.class;
else return treeTableModel.getColumnClass(column);
}
@Override
public int getRowCount() {
return tree.getRowCount();
}
protected Object nodeForRow(int row) {
TreePath treePath = tree.getPathForRow(row);
return treePath.getLastPathComponent();
}
@Override
public Object getValueAt(int row, int column) {
if(column == 0) return treeTableModel;
else return treeTableModel.getValueAt(nodeForRow(row), column);
}
@Override
public boolean isCellEditable(int row, int column) {
return treeTableModel.isCellEditable(nodeForRow(row), column);
}
@Override
public void setValueAt(Object value, int row, int column) {
Object node = nodeForRow(row);
treeTableModel.setValueAt(value, node, column);
}
}//class TreeTableModelAdapter extends AbstractTableModel
/**
* The JTree used for rendering the first column.
*/
class CustomJTree extends JTree {
@Override
public void updateUI(){
super.updateUI();
setRowHeight(0);
}
public void setVisibleRow(int row){
visibleRow = row;
}
/**
* Paints only the current cell in the table
*/
@Override
public void paint(Graphics g){
Rectangle rowBounds = getRowBounds(visibleRow);
g.translate(0, -rowBounds.y);
Rectangle oldClip = g.getClipBounds();
// Rectangle newClip = oldClip.intersection(
// new Rectangle(oldClip.x, rowBounds.y, oldClip.width,
// rowBounds.height));
// g.setClip(newClip);
//re-implemented more efficiently below:
int newY = Math.max(oldClip.y, rowBounds.y);
int newHeight = Math.min(rowBounds.height - (rowBounds.y - newY),
oldClip.height);
g.setClip(oldClip.x, newY, oldClip.width, newHeight);
super.paint(g);
}
@Override
public Dimension getPreferredSize(){
return new Dimension(super.getPreferredSize().width,
getRowBounds(visibleRow).height);
}
@Override
public void validate(){}
@Override
public void revalidate(){}
@Override
public void repaint(long tm, int x, int y, int width, int height){}
@Override
public void repaint(Rectangle r){}
protected int visibleRow;
/* (non-Javadoc)
* @see javax.swing.JTree#setRootVisible(boolean)
*/
@Override
public void setRootVisible(boolean rootVisible) {
boolean oldValue = isRootVisible();
if(oldValue != rootVisible){
super.setRootVisible(rootVisible);
modelAdapter.fireTableDataChanged();
}
}
}
/*
class SmartTreeCellRenderer implements TreeCellRenderer{
SmartTreeCellRenderer(TreeCellRenderer renderer){
originalRenderer = renderer;
}
public Component getTreeCellRendererComponent(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus){
Component comp = originalRenderer.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, hasFocus);
if(comp instanceof JComponent &&
comp.getPreferredSize().height < getRowHeight(row)){
((JComponent)comp).setPreferredSize(
new Dimension(comp.getPreferredSize().width,
getRowHeight(row))
);
}
return comp;
}
public TreeCellRenderer getOriginalRenderer(){
return originalRenderer;
}
TreeCellRenderer originalRenderer;
}
*/
}//public class JTreeTable extends XJTable