de.sciss.treetable.j.TreeTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jtreetable Show documentation
Show all versions of jtreetable Show documentation
A TreeTable component for Swing
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package de.sciss.treetable.j;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TooManyListenersException;
import java.util.AbstractMap.SimpleEntry;
import javax.swing.AbstractButton;
import javax.swing.DropMode;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.Scrollable;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.ToolTipManager;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.RowSorter.SortKey;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.plaf.UIResource;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.text.Position;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import de.sciss.treetable.j.event.TreeColumnModelEvent;
import de.sciss.treetable.j.event.TreeColumnModelListener;
import de.sciss.treetable.j.event.TreeTableMouseEvent;
import de.sciss.treetable.j.event.TreeTableMouseListener;
import de.sciss.treetable.j.event.TreeTableMouseMotionListener;
import de.sciss.treetable.j.event.TreeTableSorterEvent;
import de.sciss.treetable.j.event.TreeTableSorterListener;
import de.sciss.treetable.j.ui.BasicTreeTableUI;
import de.sciss.treetable.j.ui.TableInterface;
import de.sciss.treetable.j.ui.TreeInterface;
import de.sciss.treetable.j.ui.TreeTableUI;
public class TreeTable extends JComponent implements Scrollable {
public TreeTable() {
this(new DefaultTreeTableNode());
}
public TreeTable(TreeTableNode root) {
this(new DefaultTreeModel(root), new DefaultTreeColumnModel(root));
}
public TreeTable(TreeModel tm, TreeColumnModel rm) {
this(tm, rm, null);
}
public TreeTable(TreeModel tm, TreeColumnModel tcm, TableColumnModel cm) {
if (tm == null || tcm == null)
throw new NullPointerException();
adapter = createAdapter(tm, tcm);
columnModel = cm;
toolTipMap = getDefaultToolTipMap();
setFocusable(true);
setOpaque(true);
updateUI();
ToolTipManager.sharedInstance().registerComponent(this);
}
protected Adapter adapter;
private TreeInterface tree;
private TableInterface table;
private TableColumnModel columnModel;
private JTable rowHeader;
private TreeTableCellRenderer focusRenderer;
private Color alternateRowColor;
private IconMap iconMap;
private ToolTipMap toolTipMap;
private Icon openIcon;
private Icon closedIcon;
private Icon leafIcon;
private Icon ascendingSortIcon;
private Icon descendingSortIcon;
private HashMap,TreeTableCellRenderer> defaultRenderers;
private HashMap,TreeTableCellEditor> defaultEditors;
private HashMap sortedPaths = new HashMap();
private int rowMargin = 0;
private boolean expandsSortedNodes = true;
private boolean nodeSortingEnabled = true;
private boolean columnFocusEnabled = true;
private boolean autoCreateColumnHeader = true;
private boolean autoCreateRowHeader = false;
private boolean autoCreateRowSorter = false;
private static final String uiClassID = "TreeTableUI";
@Override
public String getUIClassID() {
return uiClassID;
}
public void setUI(TreeTableUI ui) {
boolean addExpandLis = false;
boolean addWillExpandLis = false;
if (table != null) {
table.removePropertyChangeListener(adapter);
tree.removePropertyChangeListener(adapter);
int lisCount = listenerList.getListenerCount(TreeExpansionListener.class);
if (lisCount > 0) {
tree.removeTreeExpansionListener(adapter);
addExpandLis = true;
}
lisCount = listenerList.getListenerCount(TreeWillExpandListener.class);
if (lisCount > 0) {
tree.removeTreeWillExpandListener(adapter);
addWillExpandLis = true;
}
getSelectionModel().removeTreeSelectionListener(adapter);
if (getAutoCreateColumnsFromModel())
columnModel = null;
updateUIProperties();
}
super.setUI(ui);
tree = getUI().getTreeInterface(this);
table = getUI().getTableInterface(this);
// if (columnModel == null) {
// columnModel = table.getColumnModel();
// int hc = getTreeColumnModel().getHierarchicalColumn();
// if (hc >= 0)
// columnModel.getColumn(hc).setPreferredWidth(150);
// }
getSelectionModel().addTreeSelectionListener(adapter);
table.addPropertyChangeListener(adapter);
tree.addPropertyChangeListener(adapter);
if (addExpandLis)
tree.addTreeExpansionListener(adapter);
if (addWillExpandLis)
tree.addTreeWillExpandListener(adapter);
if (getAutoCreateColumnHeader() && isDisplayable())
configureEnclosingScrollPane();
}
public TreeTableUI getUI() {
return (TreeTableUI)ui;
}
@Override
public void updateUI() {
if (UIManager.get(getUIClassID()) != null) {
setUI(UIManager.getUI(this));
} else if (ui != null && ui.getClass() == BasicTreeTableUI.class) {
updateUIProperties();
((BasicTreeTableUI)ui).updateUI();
} else {
setUI(new BasicTreeTableUI());
}
}
protected void updateUIProperties() {
if (leafIcon instanceof UIResource)
leafIcon = null;
if (openIcon instanceof UIResource)
openIcon = null;
if (closedIcon instanceof UIResource)
closedIcon = null;
if (ascendingSortIcon instanceof UIResource)
ascendingSortIcon = null;
if (descendingSortIcon instanceof UIResource)
descendingSortIcon = null;
updateUIRenderers();
updateUIEditors();
}
@SuppressWarnings("unchecked")
private void updateUIRenderers() {
if (defaultRenderers == null && columnModel == null)
return;
IdentityHashMap components = new IdentityHashMap();
if (defaultRenderers != null) {
for (Map.Entry,TreeTableCellRenderer> entry : defaultRenderers.entrySet()) {
if (entry.getValue() instanceof JComponent) {
components.put(entry.getValue(), null);
} else if (!(entry.getValue() instanceof Component)) {
Class> cls = entry.getKey();
TreeColumnModel cm = adapter.treeColumnModel;
for (int col=cm.getColumnCount(); --col>=0;) {
if (cls.isAssignableFrom(cm.getColumnClass(col))) {
addComponents(components, entry.getValue(),
convertColumnIndexToView(col));
}
}
}
}
}
if (columnModel != null) {
for (int col=columnModel.getColumnCount(); --col>=0;) {
Object r = columnModel.getColumn(col).getCellRenderer();
if (r instanceof JComponent) {
components.put(r, null);
} else if (!(r instanceof Component)
&& r instanceof TreeTableCellRenderer) {
addComponents(components, (TreeTableCellRenderer)r, col);
}
}
}
for (JComponent c : (Set)components.keySet())
c.updateUI();
}
@SuppressWarnings("unchecked")
private void addComponents(IdentityHashMap components, TreeTableCellRenderer r, int col) {
try {
int row;
Object value;
if (getRowCount() == 0) {
row = -1;
value = null;
} else {
row = 0;
value = adapter.getValueAt(0, col);
}
if (col == adapter.treeColumnModel.getHierarchicalColumn()) {
Component c;
if (row < 0) {
c = r.getTreeTableCellRendererComponent(
this, value, false, false, row, col, false, true);
} else {
TreePath path = getPathForRow(0);
c = r.getTreeTableCellRendererComponent(
this, value, false, false, row, col,
isExpanded(path), isLeaf(path));
}
if (c instanceof JComponent)
components.put(c, c);
} else {
Component c = r.getTreeTableCellRendererComponent(
this, value, false, false, row, col);
if (c instanceof JComponent)
components.put(c, c);
}
} catch (Exception e) { /* ignore */ }
}
private void updateUIEditors() {
// TODO
}
public void processTreeExpansion(TreePath path, int rowsAdded) {
int row = getRowForPath(path);
adapter.fireTableRowsInserted(row+1, row+rowsAdded);
adapter.updateSorter(path, true);
if (getRowHeight() <= 0)
updateTableRowHeights(row+1, row+rowsAdded+1);
}
public void processTreeCollapse(TreePath path, int rowsRemoved) {
int row = getRowForPath(path);
adapter.fireTableRowsDeleted(row+1, row+rowsRemoved);
adapter.updateSorter(path, false);
}
@Override
public void addNotify() {
super.addNotify();
configureEnclosingScrollPane();
}
@Override
public void removeNotify() {
super.removeNotify();
unconfigureEnclosingScrollPane();
}
private JScrollPane getScrollPaneAncestor() {
Container p = getParent();
if (p instanceof JViewport) {
Container gp = p.getParent();
if (gp instanceof JScrollPane) {
JScrollPane scrollPane = (JScrollPane)gp;
// Make certain we are the viewPort's view and not, for
// example, the rowHeaderView of the scrollPane -
// an implementor of fixed columns might do this.
JViewport viewport = scrollPane.getViewport();
if (viewport != null && viewport.getView() == this)
return scrollPane;
}
}
return null;
}
// JTable.configureEnclosingScrollPane...
protected void configureEnclosingScrollPane() {
if (!getAutoCreateColumnHeader() && !getAutoCreateRowHeader())
return;
JScrollPane scrollPane = getScrollPaneAncestor();
if (scrollPane == null)
return;
if (getAutoCreateColumnHeader()) {
scrollPane.setColumnHeaderView(getTableHeader());
// scrollPane.getViewport().setBackingStoreEnabled(true);
Border border = scrollPane.getBorder();
if (border == null || border instanceof UIResource) {
Border scrollPaneBorder =
UIManager.getBorder("Table.scrollPaneBorder");
if (scrollPaneBorder != null) {
scrollPane.setBorder(scrollPaneBorder);
}
}
}
if (getAutoCreateRowHeader()) {
createRowHeader();
scrollPane.setRowHeaderView(rowHeader);
}
}
// JTable.unconfigureEnclosingScrollPane...
protected void unconfigureEnclosingScrollPane() {
if (!getAutoCreateColumnHeader() && !getAutoCreateRowHeader())
return;
JScrollPane scrollPane = getScrollPaneAncestor();
if (scrollPane == null)
return;
if (getAutoCreateColumnHeader())
scrollPane.setColumnHeaderView(null);
if (getAutoCreateRowHeader()) {
scrollPane.setRowHeaderView(null);
rowHeader = null;
}
}
private void createRowHeader() {
RowHeaderAdapter adapter = new RowHeaderAdapter(getTableModel());
rowHeader = new JTable(adapter, null, getRowSelectionModel());
rowHeader.addPropertyChangeListener("UI", adapter);
rowHeader.setAutoCreateColumnsFromModel(false);
rowHeader.setRowMargin(0);
rowHeader.getColumnModel().setColumnMargin(0);
rowHeader.setFocusable(false);
TreeTable.updateRowHeaderRenderer(rowHeader);
int ht = getRowHeight();
if (ht > 0) {
rowHeader.setRowHeight(ht);
} else {
updateRowHeaderHeights(0, getRowCount());
}
}
private static void updateRowHeaderRenderer(JTable rowHeader) {
TableCellRenderer r = rowHeader.getTableHeader().getDefaultRenderer();
if (r instanceof JLabel) {
JLabel l = (JLabel)r;
l.setHorizontalAlignment(JLabel.CENTER);
}
rowHeader.getColumnModel().getColumn(0).setCellRenderer(r);
Dimension size = r.getTableCellRendererComponent(
rowHeader, "9999", false, false, -1, -1).getPreferredSize();
rowHeader.setPreferredScrollableViewportSize(size);
}
private void updateRowHeaderHeights(int fromRow, int toRow) {
for (int row=fromRow; row void addListener(Class cls, T l) {
if (l == null)
return;
int count = listenerList.getListenerCount(cls);
listenerList.add(cls, l);
if (count == 0) {
if (cls == TreeExpansionListener.class) {
tree.addTreeExpansionListener(adapter);
} else if (cls == TreeWillExpandListener.class) {
tree.addTreeWillExpandListener(adapter);
} else if (cls == TreeTableMouseListener.class) {
hasTreeTableMouseListener = true;
} else if (cls == TreeTableMouseMotionListener.class) {
hasTreeTableMouseMotionListener = true;
}
}
}
private void removeListener(Class cls, T l) {
if (l == null)
return;
listenerList.remove(cls, l);
int count = listenerList.getListenerCount(cls);
if (count == 0) {
if (cls == TreeExpansionListener.class) {
tree.removeTreeExpansionListener(adapter);
} else if (cls == TreeWillExpandListener.class) {
tree.removeTreeWillExpandListener(adapter);
} else if (cls == TreeTableMouseListener.class) {
hasTreeTableMouseListener = false;
} else if (cls == TreeTableMouseMotionListener.class) {
hasTreeTableMouseMotionListener = false;
}
}
}
public void addTreeExpansionListener(TreeExpansionListener l) {
addListener(TreeExpansionListener.class, l);
}
public void removeTreeExpansionListener(TreeExpansionListener l) {
removeListener(TreeExpansionListener.class, l);
}
private void fireTreeExpansionEvent(TreePath path, boolean exp) {
TreeExpansionEvent e = new TreeExpansionEvent(this, path);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeExpansionListener.class) {
TreeExpansionListener lis = (TreeExpansionListener)listeners[i+1];
if (exp) {
lis.treeExpanded(e);
} else {
lis.treeCollapsed(e);
}
}
}
}
public void addTreeWillExpandListener(TreeWillExpandListener l) {
addListener(TreeWillExpandListener.class, l);
}
public void removeTreeWillExpandListener(TreeWillExpandListener l) {
removeListener(TreeWillExpandListener.class, l);
}
private void fireTreeWillExpandEvent(TreePath path, boolean exp) throws ExpandVetoException {
TreeExpansionEvent e = new TreeExpansionEvent(this, path);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeWillExpandListener.class) {
TreeWillExpandListener lis = (TreeWillExpandListener)listeners[i+1];
if (exp) {
lis.treeWillExpand(e);
} else {
lis.treeWillCollapse(e);
}
}
}
}
/**
* Provides extra information about the mouse event, see
* TreeTableMouseEvent for more details. Consuming the
* TreeTableMouseEvent will also consume the source MouseEvent.
* TreeTableMouseListeners are notified before MouseListeners.
*
* @see #addTreeTableMouseMotionListener(TreeTableMouseMotionListener)
* @see #addMouseListener(java.awt.event.MouseListener)
*/
public void addTreeTableMouseListener(TreeTableMouseListener l) {
addListener(TreeTableMouseListener.class, l);
}
public void removeTreeTableMouseListener(TreeTableMouseListener l) {
removeListener(TreeTableMouseListener.class, l);
}
/**
* Provides extra information about the mouse event, see
* TreeTableMouseEvent for more details. Consuming the
* TreeTableMouseEvent will also consume the source MouseEvent.
* TreeTableMouseMotionListeners are notified before MouseMotionListeners.
*
* @see #addTreeTableMouseListener(TreeTableMouseListener)
* @see #addMouseMotionListener(java.awt.event.MouseMotionListener)
*/
public void addTreeTableMouseMotionListener(TreeTableMouseMotionListener l) {
addListener(TreeTableMouseMotionListener.class, l);
}
public void removeTreeTableMouseMotionListener(TreeTableMouseMotionListener l) {
removeListener(TreeTableMouseMotionListener.class, l);
}
@Override
protected void processMouseEvent(MouseEvent e) {
if (hasTreeTableMouseListener)
fireTreeTableMouseEvent(e);
super.processMouseEvent(e);
}
private void fireTreeTableMouseEvent(MouseEvent e) {
switch (e.getID()) {
case MouseEvent.MOUSE_ENTERED: case MouseEvent.MOUSE_EXITED:
return;
}
TreeTableMouseEvent evt = new TreeTableMouseEvent(this, e);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeTableMouseListener.class) {
TreeTableMouseListener lis = (TreeTableMouseListener)listeners[i+1];
switch (e.getID()) {
case MouseEvent.MOUSE_PRESSED:
lis.mousePressed(evt); break;
case MouseEvent.MOUSE_RELEASED:
lis.mouseReleased(evt); break;
case MouseEvent.MOUSE_CLICKED:
lis.mouseClicked(evt); break;
}
}
}
if (evt.isConsumed())
e.consume();
}
@Override
protected void processMouseMotionEvent(MouseEvent e) {
if (hasTreeTableMouseMotionListener)
fireTreeTableMouseMotionEvent(e);
super.processMouseMotionEvent(e);
}
private void fireTreeTableMouseMotionEvent(MouseEvent e) {
TreeTableMouseEvent evt = new TreeTableMouseEvent(this, e);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeTableMouseMotionListener.class) {
TreeTableMouseMotionListener lis = (TreeTableMouseMotionListener)listeners[i+1];
switch (e.getID()) {
case MouseEvent.MOUSE_MOVED:
lis.mouseMoved(evt); break;
case MouseEvent.MOUSE_DRAGGED:
lis.mouseDragged(evt); break;
}
}
}
if (evt.isConsumed())
e.consume();
}
@Override
public void doLayout() {
table.doLayout();
tree.doLayout();
super.doLayout();
}
public void invalidateAllRows() {
TreeModel model = adapter;
TreePath root = new TreePath(model.getRoot());
if (tree.isRootVisible())
adapter.invalidatePaths(root, null, null);
Enumeration paths = tree.getExpandedDescendants(root);
if (paths == null)
return;
while (paths.hasMoreElements()) {
TreePath path = paths.nextElement();
Object parent = path.getLastPathComponent();
int count = model.getChildCount(parent);
if (count > 0) {
int[] childIndices = new int[count];
Object[] childNodes = new Object[count];
for (int i=count; --i>=0;) {
childIndices[i] = i;
childNodes[i] = model.getChild(parent, i);
}
adapter.invalidatePaths(path, childIndices, childNodes);
}
}
}
public void invalidatePath(TreePath path) {
adapter.invalidatePath(path);
if (getRowHeight() <= 0) {
int row = getRowForPath(path);
updateTableRowHeights(row, row+1);
}
}
/**
* For variable row heights, sync table row height to
* corresponding tree row height.
*/
protected void updateTableRowHeights() {
updateTableRowHeights(0, tree.getRowCount());
}
/**
* Sync table row heights to corresponding tree row height
* for rows fromRow
(inclusive) to
* toRow
exclusive.
*/
protected void updateTableRowHeights(int fromRow, int toRow) {
assert (tree.getRowHeight() <= 0);
for (int row=toRow; --row>=fromRow;)
table.setRowHeight(row, getRowBounds(row).height);
if (rowHeader != null)
updateRowHeaderHeights(fromRow, toRow);
}
public boolean getAutoCreateColumnHeader() {
return autoCreateColumnHeader;
}
public void setAutoCreateColumnHeader(boolean autoCreateColumnHeader) {
boolean oldValue = getAutoCreateColumnHeader();
this.autoCreateColumnHeader = autoCreateColumnHeader;
firePropertyChange("autoCreateColumnHeader", oldValue, getAutoCreateColumnHeader());
}
public boolean getAutoCreateRowHeader() {
return autoCreateRowHeader;
}
public void setAutoCreateRowHeader(boolean autoCreateRowHeader) {
boolean oldValue = getAutoCreateRowHeader();
this.autoCreateRowHeader = autoCreateRowHeader;
firePropertyChange("autoCreateRowHeader", oldValue, getAutoCreateRowHeader());
}
public TreeTableCellRenderer getFocusRenderer() {
return focusRenderer;
}
public void setFocusRenderer(TreeTableCellRenderer renderer) {
TreeTableCellRenderer oldValue = getFocusRenderer();
focusRenderer = renderer;
this.firePropertyChange("focusRenderer", oldValue, getFocusRenderer());
if (isValid())
repaint(getLeadSelectionPath());
}
public ToolTipMap getDefaultToolTipMap() {
return adapter;
}
public ToolTipMap getToolTipMap() {
return toolTipMap;
}
public void setToolTipMap(ToolTipMap toolTipMap) {
ToolTipMap oldValue = getToolTipMap();
this.toolTipMap = toolTipMap;
firePropertyChange("toolTipMap", oldValue, getToolTipMap());
}
public String getToolTipText(MouseEvent e) {
return getToolTipMap() == null ? getToolTipText() :
getToolTipMap().getToolTipText(this, e);
}
public TableColumnModel getColumnModel() {
return columnModel;
}
public void setColumnModel(TableColumnModel columnModel) {
TableColumnModel oldValue = getColumnModel();
this.columnModel = columnModel;
firePropertyChange("columnModel", oldValue, getColumnModel());
}
public TreeModel getTreeModel() {
return adapter.treeModel;
}
public void setTreeModel(TreeModel treeModel) {
TreeModel oldValue = getTreeModel();
adapter.treeModel = treeModel;
firePropertyChange("treeModel", oldValue, getTreeModel());
if (getAutoCreateRowSorter())
autoCreateRowSorter();
}
public TreeColumnModel getTreeColumnModel() {
return adapter.treeColumnModel;
}
public void setTreeColumnModel(TreeColumnModel treeColumnModel) {
TreeColumnModel oldValue = getTreeColumnModel();
adapter.treeColumnModel = treeColumnModel;
adapter.fireTableStructureChanged();
firePropertyChange("treeColumnModel", oldValue, getTreeColumnModel());
if (getAutoCreateRowSorter())
autoCreateRowSorter();
}
/**
* Changes to the TreeModel/RowModel as well as row insertion/removal
* due to tree expansion/collapse can be listened to in terms of a
* TableModelListener by adding the TableModelListener to this TableModel.
*
* @return TableModel view of the TreeModel/RowModel combination.
* @see #getTreeModel()
* @see #getTreeColumnModel()
*/
public TableModel getTableModel() {
return adapter;
}
public TreeTableModel getTreeTableModel() {
return adapter;
}
public TreeTableSorter extends TreeModel, ? extends TreeColumnModel> getRowSorter() {
return adapter.rowSorter;
}
public void setRowSorter(TreeTableSorter extends TreeModel, ? extends TreeColumnModel> rowSorter) {
TreeTableSorter,?> oldValue = getRowSorter();
adapter.setRowSorter(rowSorter);
firePropertyChange("rowSorter", oldValue, getRowSorter());
}
public boolean isNodeSortingEnabled() {
return nodeSortingEnabled;
}
public void setNodeSortingEnabled(boolean nodeSortingEnabled) {
boolean oldValue = isNodeSortingEnabled();
this.nodeSortingEnabled = nodeSortingEnabled;
firePropertyChange("nodeSortingEnabled", oldValue, isNodeSortingEnabled());
if (!sortedPaths.isEmpty())
repaint();
}
public boolean getAutoCreateRowSorter() {
return autoCreateRowSorter;
}
public void setAutoCreateRowSorter(boolean autoCreateRowSorter) {
boolean oldValue = getAutoCreateRowSorter();
this.autoCreateRowSorter = autoCreateRowSorter;
if (getAutoCreateRowSorter())
autoCreateRowSorter();
firePropertyChange("autoCreateRowSorter", oldValue, getAutoCreateRowSorter());
}
private void autoCreateRowSorter() {
setRowSorter(new DefaultTreeTableSorter(
getTreeModel(), getTreeColumnModel()));
}
public RowSorter> getSorterForPath(TreePath path) {
return adapter.getSorter(path);
}
/**
* @return columnFocusEnabed
* @see #setColumnFocusEnabled(boolean)
*/
public boolean isColumnFocusEnabled() {
return columnFocusEnabled;
}
/**
* If false, the focus is draw around the entire focused row.
*/
public void setColumnFocusEnabled(boolean columnFocusEnabled) {
boolean oldValue = isColumnFocusEnabled();
this.columnFocusEnabled = columnFocusEnabled;
firePropertyChange("columnFocusEnabled", oldValue, isColumnFocusEnabled());
repaint(getLeadSelectionPath());
}
public TableColumn getColumn(Object identifier) {
return columnModel.getColumn(columnModel.getColumnIndex(identifier));
}
public Color getAlternateRowColor() {
return alternateRowColor;
}
public void setAlternateRowColor(Color alternateRowColor) {
Color oldValue = getAlternateRowColor();
this.alternateRowColor = alternateRowColor;
firePropertyChange("alternateRowColor", oldValue, getAlternateRowColor());
}
public void setDefaultRenderer(Class> columnClass, TreeTableCellRenderer renderer) {
defaultRenderers = putDefault(columnClass, renderer, defaultRenderers);
}
public TreeTableCellRenderer getDefaultRenderer(Class> columnClass) {
TreeTableCellRenderer r = defaultRenderers == null ? null :
getDefault(columnClass, defaultRenderers);
return r != null ? r : ui == null ? null :
getUI().getDefaultRenderer(this, columnClass);
}
public void setDefaultEditor(Class> columnClass, TreeTableCellEditor editor) {
defaultEditors = putDefault(columnClass, editor, defaultEditors);
}
public TreeTableCellEditor getDefaultEditor(Class> columnClass, int column) {
TreeTableCellEditor e = defaultEditors == null ? null :
getDefault(columnClass, defaultEditors);
return e != null ? e : ui == null ? null :
getUI().getDefaultEditor(this, columnClass, column);
}
private static HashMap,T> putDefault(
Class> key, T value, HashMap,T> defaults) {
if (defaults == null) {
if (value == null)
return null;
defaults = new HashMap,T>(8);
}
if (value != null) {
defaults.put(key, value);
} else {
defaults.remove(key);
}
return defaults;
}
private static T getDefault(Class> cls, HashMap,T> defaults) {
if (cls == null)
return null;
T def = defaults.get(cls);
return def != null ? def : getDefault(cls.getSuperclass(), defaults);
}
public TreeTableCellRenderer getCellRenderer(int row, int column) {
if (column >= 0 && column < getColumnModel().getColumnCount()) {
TableCellRenderer renderer = getColumnModel()
.getColumn(column).getCellRenderer();
if (renderer instanceof TreeTableCellRenderer)
return (TreeTableCellRenderer)renderer;
return getDefaultRenderer(getTreeColumnModel().getColumnClass(
convertColumnIndexToModel(column)));
}
return getDefaultRenderer(Object.class);
}
public TreeTableCellEditor getCellEditor(int row, int column) {
if (column >= 0 && column < getColumnModel().getColumnCount()) {
TableCellEditor editor = getColumnModel()
.getColumn(column).getCellEditor();
if (editor instanceof TreeTableCellEditor)
return (TreeTableCellEditor)editor;
return getDefaultEditor(getTreeColumnModel().getColumnClass(
convertColumnIndexToModel(column)), column);
}
return getDefaultEditor(Object.class, column);
}
public boolean isCellEditable(int row, int column) {
return adapter.isCellEditable(row, convertColumnIndexToModel(column));
}
public boolean startEditingAtPath(TreePath path) {
return editCellAt(getRowForPath(path), getHierarchicalColumn());
}
public boolean startEditingAtRow(int row) {
return editCellAt(row, getHierarchicalColumn());
}
public boolean editCellAt(int row, int column) {
return table.editCellAt(row, column);
}
public boolean isEditing() {
return table.isEditing();
}
public Component getEditorComponent() {
return table.getEditorComponent();
}
public int getEditingColumn() {
return table.getEditingColumn();
}
public int getEditingRow() {
return table.getEditingRow();
}
public TreeTableCellEditor getCellEditor() {
return (TreeTableCellEditor)table.getCellEditor();
}
/**
* @return the hierarchical column in view coordinates
*/
public int getHierarchicalColumn() {
return convertColumnIndexToView(
getTreeColumnModel().getHierarchicalColumn());
}
public int convertColumnIndexToView(int modelColumnIndex) {
return table.convertColumnIndexToView(modelColumnIndex);
}
public int convertColumnIndexToModel(int viewColumnIndex) {
return table.convertColumnIndexToModel(viewColumnIndex);
}
public int convertNodeIndexToView(Object parent, int modelIndex) {
return adapter.convertIndexToView(parent, modelIndex);
}
public int convertNodeIndexToModel(Object parent, int viewIndex) {
return adapter.convertIndexToModel(parent, viewIndex);
}
public Object getValueAt(int row, int column) {
return adapter.getValueAt(row, convertColumnIndexToModel(column));
}
public Object getNode(int row) {
return adapter.getNode(row);
}
public Icon getLeafIcon() {
if (leafIcon == null)
leafIcon = UIManager.getIcon("Tree.leafIcon");
return leafIcon;
}
public void setLeafIcon(Icon leafIcon) {
Icon oldValue = this.leafIcon;
this.leafIcon = leafIcon;
firePropertyChange("leafIcon", oldValue, getLeafIcon());
repaintColumn(getHierarchicalColumn());
}
public Icon getOpenIcon() {
if (openIcon == null)
openIcon = UIManager.getIcon("Tree.openIcon");
return openIcon;
}
public void setOpenIcon(Icon openIcon) {
Icon oldValue = this.openIcon;
this.openIcon = openIcon;
firePropertyChange("openIcon", oldValue, getOpenIcon());
repaintColumn(getHierarchicalColumn());
}
public Icon getClosedIcon() {
if (closedIcon == null)
closedIcon = UIManager.getIcon("Tree.closedIcon");
return closedIcon;
}
public void setClosedIcon(Icon closedIcon) {
Icon oldValue = this.closedIcon;
this.closedIcon = closedIcon;
firePropertyChange("closedIcon", oldValue, getClosedIcon());
repaintColumn(getHierarchicalColumn());
}
private void repaintColumn(int col) {
if (col < 0 || getRowCount() == 0)
return;
Rectangle r = getCellRect(0, col, true);
r.height = getHeight();
repaint(r);
}
public IconMap getIconMap() {
return iconMap;
}
public void setIconMap(IconMap iconMap) {
IconMap oldValue = getIconMap();
this.iconMap = iconMap;
firePropertyChange("iconMap", oldValue, getIconMap());
if (isValid())
repaintColumn(getHierarchicalColumn());
}
public Icon getAscendingSortIcon() {
if (ascendingSortIcon == null)
ascendingSortIcon = UIManager.getIcon("Table.ascendingSortIcon");
return ascendingSortIcon;
}
public void setAscendingSortIcon(Icon ascendingSortIcon) {
Icon oldValue = getAscendingSortIcon();
this.ascendingSortIcon = ascendingSortIcon;
firePropertyChange("ascendingSortIcon", oldValue, getAscendingSortIcon());
}
public Icon getDescendingSortIcon() {
if (descendingSortIcon == null)
descendingSortIcon = UIManager.getIcon("Table.descendingSortIcon");
return descendingSortIcon;
}
public void setDescendingSortIcon(Icon descendingSortIcon) {
Icon oldValue = getDescendingSortIcon();
this.descendingSortIcon = descendingSortIcon;
firePropertyChange("descendingSortIcon", oldValue, getDescendingSortIcon());
}
public Icon getIconForPath(TreePath path) {
return getIcon(path.getLastPathComponent(), isExpanded(path), isLeaf(path));
}
public Icon getIconForRow(int row) {
return getIconForPath(getPathForRow(row));
}
public Icon getIcon(Object node, boolean expanded, boolean leaf) {
Icon icon = iconMap == null ? null :
iconMap.getIcon(this, node, expanded, leaf);
if (icon == null) {
if (leaf) {
icon = getLeafIcon();
} else if (expanded) {
icon = getOpenIcon();
} else {
icon = getClosedIcon();
}
}
return icon;
}
public boolean getAutoCreateColumnsFromModel() {
return table.getAutoCreateColumnsFromModel();
}
public void setAutoCreateColumnsFromModel(boolean autoCreateColumnsFromModel) {
table.setAutoCreateColumnsFromModel(autoCreateColumnsFromModel);
}
public int getAutoResizeMode() {
return table.getAutoResizeMode();
}
public void setAutoResizeMode(int mode) {
table.setAutoResizeMode(mode);
}
public boolean getCellSelectionEnabled() {
return table.getCellSelectionEnabled();
}
public void setCellSelectionEnabled(boolean cellSelectionEnabled) {
table.setCellSelectionEnabled(cellSelectionEnabled);
if (getDragEnabled())
updateDropMode();
}
public boolean getColumnSelectionAllowed() {
return table.getColumnSelectionAllowed();
}
public void setColumnSelectionAllowed(boolean columnSelectionAllowed) {
table.setColumnSelectionAllowed(columnSelectionAllowed);
if (getDragEnabled())
updateDropMode();
}
public Color getGridColor() {
return table.getGridColor();
}
public void setGridColor(Color gridColor) {
table.setGridColor(gridColor);
}
public Dimension getIntercellSpacing() {
return table.getIntercellSpacing();
}
public void setIntercellSpacing(Dimension intercellSpacing) {
table.setIntercellSpacing(intercellSpacing);
}
public int getRowMargin() {
return rowMargin;
}
public void setRowMargin(int rowMargin) {
int oldValue = getRowMargin();
this.rowMargin = rowMargin;
firePropertyChange("rowMargin", oldValue, getRowMargin());
if (isValid())
repaint();
}
public boolean getRowSelectionAllowed() {
return table.getRowSelectionAllowed();
}
public void setRowSelectionAllowed(boolean rowSelectionAllowed) {
table.setRowSelectionAllowed(rowSelectionAllowed);
if (getDragEnabled())
updateDropMode();
}
public boolean getShowHorizontalLines() {
return table.getShowHorizontalLines();
}
public void setShowHorizontalLines(boolean showHorizontalLines) {
table.setShowHorizontalLines(showHorizontalLines);
}
public boolean getShowVerticalLines() {
return table.getShowVerticalLines();
}
public void setShowVerticalLines(boolean showVerticalLines) {
table.setShowVerticalLines(showVerticalLines);
}
public void setShowGrid(boolean showGrid) {
table.setShowGrid(showGrid);
}
public JTableHeader getTableHeader() {
return table.getTableHeader();
}
public void setTableHeader(JTableHeader tableHeader) {
table.setTableHeader(tableHeader);
}
public Enumeration getExpandedDescendants(TreePath parent) {
return tree.getExpandedDescendants(parent);
}
public void collapsePath(TreePath path) {
tree.collapsePath(path);
}
public void collapseRow(int row) {
collapsePath(getPathForRow(row));
}
public void expandPath(TreePath path) {
tree.expandPath(path);
}
public void expandRow(int row) {
expandPath(getPathForRow(row));
}
public void makeVisible(TreePath path) {
tree.makeVisible(path);
}
public void scrollPathToVisible(TreePath path) {
tree.scrollPathToVisible(path);
}
public void scrollRowToVisible(int row) {
scrollPathToVisible(getPathForRow(row));
}
public Rectangle getPathBounds(TreePath path) {
return ui == null ? null : getUI().getPathBounds(this, path);
}
public TreePath getPathForLocation(int x, int y) {
return ui == null ? null : getUI().getPathForLocation(this, x, y);
}
public TreePath getClosestPathForLocation(int x, int y) {
return ui == null ? null : getUI().getClosestPathForLocation(this, x, y);
}
public TreePath getPathForRow(int row) {
return tree.getPathForRow(row);
}
public Rectangle getRowBounds(int row) {
return getPathBounds(getPathForRow(row));
}
public int getRowCount() {
return tree.getRowCount();
}
public int getColumnCount() {
return columnModel.getColumnCount();
}
public int getRowForLocation(int x, int y) {
TreePath path = getPathForLocation(x, y);
return path == null ? -1 : getRowForPath(path);
}
public int getClosestRowForLocation(int x, int y) {
return tree.getClosestRowForLocation(x, y);
}
public int getRowForPath(TreePath path) {
return tree.getRowForPath(path);
}
public boolean isCollapsed(int row) {
return isCollapsed(getPathForRow(row));
}
public boolean isCollapsed(TreePath path) {
return tree.isCollapsed(path);
}
public boolean isExpanded(int row) {
return isExpanded(getPathForRow(row));
}
public boolean isExpanded(TreePath path) {
return tree.isExpanded(path);
}
public boolean hasBeenExpanded(TreePath path) {
return tree.hasBeenExpanded(path);
}
public boolean isFixedRowHeight() {
return tree.isFixedRowHeight();
}
public boolean isLargeModel() {
return tree.isLargeModel();
}
public void setLargeModel(boolean largeModel) {
tree.setLargeModel(largeModel);
}
public boolean isRootVisible() {
return tree.isRootVisible();
}
public void setRootVisible(boolean rootVisible) {
tree.setRootVisible(rootVisible);
}
public boolean getScrollsOnExpand() {
return tree.getScrollsOnExpand();
}
public void setScrollsOnExpand(boolean scrollsOnExpand) {
tree.setScrollsOnExpand(scrollsOnExpand);
}
public boolean getShowsRootHandles() {
return tree.getShowsRootHandles();
}
public void setShowsRootHandles(boolean newValue) {
tree.setShowsRootHandles(newValue);
}
public void setToggleClickCount(int clickCount) {
tree.setToggleClickCount(clickCount);
}
public int getToggleClickCount() {
return tree.getToggleClickCount();
}
public int getVisibleRowCount() {
return tree.getVisibleRowCount();
}
public void setVisibleRowCount(int newCount) {
tree.setVisibleRowCount(newCount);
}
public int getRowHeight() {
return tree.getRowHeight();
}
public void setRowHeight(int rowHeight) {
int oldRowHeight = getRowHeight();
if (rowHeight > 0) {
table.setRowHeight(rowHeight);
if (rowHeader != null)
rowHeader.setRowHeight(rowHeight);
}
tree.setRowHeight(rowHeight);
if (rowHeight <= 0 && oldRowHeight > 0)
updateTableRowHeights();
}
public int getRowHeight(int row) {
return table.getRowHeight(row);
}
public TreeSelectionModel getSelectionModel() {
return tree == null ? null : tree.getSelectionModel();
}
public void setSelectionModel(TreeSelectionModel selectionModel) {
if (tree != null)
tree.setSelectionModel(selectionModel);
}
/**
* Changes to the TreeSelectionModel can be listened to
* in terms of a ListSelectionModel by adding a
* ListSelectionListener to this ListSelectionModel.
*
* @return ListSelectionModel view of the TreeSelectionModel.
* @see #getSelectionModel()
*/
public ListSelectionModel getRowSelectionModel() {
return adapter;
}
public void clearSelection() {
tree.clearSelection();
}
public boolean isSelectionEmpty() {
return tree.isSelectionEmpty();
}
public int getSelectionCount() {
return tree.getSelectionCount();
}
public int getMaxSelectionRow() {
return tree.getMaxSelectionRow();
}
public int getMinSelectionRow() {
return tree.getMinSelectionRow();
}
public boolean isPathSelected(TreePath path) {
return tree.isPathSelected(path);
}
public boolean isRowSelected(int row) {
return isPathSelected(getPathForRow(row));
}
/**
* @param column the column index
* @return true if column
is selected
*/
public boolean isColumnSelected(int column) {
return columnModel.getSelectionModel().isSelectedIndex(column);
}
/**
* @param row the row index
* @param column the column index
* @return true if the cell for (row, column)
is selected
*/
public boolean isCellSelected(int row, int column) {
if (!getRowSelectionAllowed() && !getColumnSelectionAllowed()) {
return false;
}
return (!getRowSelectionAllowed() || isRowSelected(row)) &&
(!getColumnSelectionAllowed() || isColumnSelected(column));
}
public TreePath getAnchorSelectionPath() {
return tree.getAnchorSelectionPath();
}
public void setAnchorSelectionPath(TreePath newPath) {
tree.setAnchorSelectionPath(newPath);
}
public TreePath getLeadSelectionPath() {
return tree.getLeadSelectionPath();
}
public void setLeadSelectionPath(TreePath newPath) {
tree.setLeadSelectionPath(newPath);
}
public int getLeadSelectionRow() {
return tree.getLeadSelectionRow();
}
public int getLeadSelectionColumn() {
return columnModel.getSelectionModel().getLeadSelectionIndex();
}
public boolean getExpandsSelectedPaths() {
return tree.getExpandsSelectedPaths();
}
public void setExpandsSelectedPaths(boolean newValue) {
tree.setExpandsSelectedPaths(newValue);
}
public TreePath getSelectionPath() {
return tree.getSelectionPath();
}
public void setSelectionPath(TreePath path) {
tree.setSelectionPath(path);
}
public int getSelectedRow() {
return getRowForPath(getSelectionPath());
}
public TreePath[] getSelectionPaths() {
return tree.getSelectionPaths();
}
public void setSelectionPaths(TreePath[] paths) {
tree.setSelectionPaths(paths);
}
public int[] getSelectionRows() {
return tree.getSelectionRows();
}
public int[] getSelectedRows() {
return getSelectionRows();
}
public void addSelectionInterval(int index0, int index1) {
tree.addSelectionInterval(index0, index1);
}
public void addSelectionPath(TreePath path) {
tree.addSelectionPath(path);
}
public void addSelectionPaths(TreePath[] paths) {
tree.addSelectionPaths(paths);
}
public void addSelectionRow(int row) {
addSelectionPath(getPathForRow(row));
}
public void addSelectionRows(int[] rows) {
tree.addSelectionRows(rows);
}
public void removeSelectionInterval(int index0, int index1) {
tree.removeSelectionInterval(index0, index1);
}
public void removeSelectionPath(TreePath path) {
tree.removeSelectionPath(path);
}
public void removeSelectionPaths(TreePath[] paths) {
tree.removeSelectionPaths(paths);
}
public void removeSelectionRow(int row) {
removeSelectionPath(getPathForRow(row));
}
public void removeSelectionRows(int[] rows) {
tree.removeSelectionRows(rows);
}
public void setSelectionRows(int[] rows) {
tree.setSelectionRows(rows);
}
public void setSelectionRow(int row) {
setSelectionPath(getPathForRow(row));
}
public void setSelectionInterval(int index0, int index1) {
tree.setSelectionInterval(index0, index1);
}
public void changeSelection(int row, int column, boolean toggle, boolean extend) {
table.changeSelection(row, column, toggle, extend);
}
public Color getSelectionForeground() {
return table.getSelectionForeground();
}
public void setSelectionForeground(Color selectionForeground) {
table.setSelectionForeground(selectionForeground);
}
public Color getSelectionBackground() {
return table.getSelectionBackground();
}
public void setSelectionBackground(Color selectionBackground) {
table.setSelectionBackground(selectionBackground);
}
public int getSelectedRowCount() {
return getSelectionCount();
}
public int getSelectedColumn() {
return columnModel.getSelectionModel().getMinSelectionIndex();
}
public int getSelectedColumnCount() {
return columnModel.getSelectedColumnCount();
}
public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
return table.getCellBounds(row, column, includeSpacing);
}
public int columnAtPoint(Point pt) {
return table.columnAtPoint(pt);
}
public int rowAtPoint(Point pt) {
return table.rowAtPoint(pt);
}
// Scrollable interface
public Dimension getPreferredScrollableViewportSize() {
Dimension size;
if (adapter.treeColumnModel.getHierarchicalColumn() < 0) {
int ht = getRowHeight();
if (ht <= 0)
ht = 20;
size = new Dimension(tree.getVisibleRowCount()*ht, 0);
} else {
size = tree.getPreferredScrollableViewportSize();
}
TableColumnModel cm = getColumnModel();
int width = 0;
for (int col=cm.getColumnCount(); --col>=0;)
width += cm.getColumn(col).getPreferredWidth();
size.width = width + cm.getColumnMargin() * cm.getColumnCount();
return size;
}
/**
* JTree and JTable have different scrolling behavior,
* so scroll as if a JTree for vertical scrolls and
* as a JTable for horizontal scrolls.
*
* @param orientation VERTICAL or HORIZONTAL
* @return tree for VERTICAL, table for HORIZONTAL
*/
private Scrollable getScrollable(int orientation) {
return orientation == SwingConstants.VERTICAL ? tree : table;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
return getScrollable(orientation).getScrollableBlockIncrement(
visibleRect, orientation, direction);
}
@Override
public boolean getScrollableTracksViewportHeight() {
return table.getScrollableTracksViewportHeight();
}
@Override
public boolean getScrollableTracksViewportWidth() {
return table.getScrollableTracksViewportWidth();
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction) {
return getScrollable(orientation).getScrollableUnitIncrement(
visibleRect, orientation, direction);
}
void repaint(TreePath path, int col) {
if (col < 0) {
repaint(path);
} else if (path != null) {
repaint(getCellRect(getRowForPath(path), col, true));
}
}
void repaint(TreePath path) {
if (path == null)
return;
Rectangle r = tree.getPathBounds(path);
if (r == null)
return;
r.x = 0;
r.width = getWidth();
repaint(r);
}
protected Adapter createAdapter(TreeModel tm, TreeColumnModel tcm) {
return new Adapter(tm, tcm);
}
protected class Adapter extends AbstractTableModel implements
TreeTableModel, TreeModelListener, TreeColumnModelListener,
ListSelectionModel, PropertyChangeListener, TreeTableSorterListener,
TreeExpansionListener, TreeSelectionListener, TreeWillExpandListener,
ToolTipMap {
Adapter() {}
public Adapter(TreeModel tm, TreeColumnModel tcm) {
treeModel = tm;
treeColumnModel = tcm;
treeRoot = tm.getRoot();
tm.addTreeModelListener(this);
tcm.addTreeColumnModelListener(this);
}
protected TreeModel treeModel;
protected TreeColumnModel treeColumnModel;
/**
* Used to determine when TreeModel's root has changed.
*/
protected Object treeRoot;
private TreeTableSorter extends TreeModel, ? extends TreeColumnModel> rowSorter;
private boolean ignoreSortedChange = false;
public void setRowSorter(TreeTableSorter extends TreeModel, ? extends TreeColumnModel> sorter) {
if (rowSorter != null)
rowSorter.removeTreeTableSorterListener(this);
rowSorter = sorter;
if (sorter != null)
sorter.addTreeTableSorterListener(this);
}
// TableModel interface
@Override
public int getColumnCount() {
return treeColumnModel.getColumnCount();
}
@Override
public String getColumnName(int column) {
return treeColumnModel.getColumnName(column);
}
@Override
public Class> getColumnClass(int column) {
return treeColumnModel.getColumnClass(column);
}
@Override
public int getRowCount() {
return tree.getRowCount();
}
public Object getNode(int row) {
return getPathForRow(row).getLastPathComponent();
}
@Override
public Object getValueAt(int row, int column) {
return treeColumnModel.getValueAt(getNode(row), column);
}
@Override
public void setValueAt(Object value, int row, int column) {
treeColumnModel.setValueAt(value, getNode(row), column);
}
@Override
public boolean isCellEditable(int row, int column) {
return treeColumnModel.isCellEditable(getNode(row), column);
}
@Override
public void fireTableStructureChanged() {
super.fireTableStructureChanged();
if (getRowHeight() <= 0)
updateTableRowHeights();
}
@Override
public void fireTableDataChanged() {
super.fireTableDataChanged();
if (getRowHeight() <= 0)
updateTableRowHeights();
}
// TreeColumnModelListener interface
@Override
public void treeColumnChanged(TreeColumnModelEvent e) {
TreePath path = e.getTreePath();
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(path.getLastPathComponent());
if (sorter != null && path.getParentPath() != null) {
boolean isc = ignoreSortedChange;
ignoreSortedChange = true;
try {
Object parent = path.getParentPath().getLastPathComponent();
Object node = path.getLastPathComponent();
int modelRow = treeModel.getIndexOfChild(parent, node);
int viewRow = sorter.convertRowIndexToView(modelRow);
sorter.rowsUpdated(modelRow, modelRow);
if (sorter.convertRowIndexToView(modelRow) != viewRow) {
if (isExpanded(path)) {
updatePath(path, expandedDescendants(path));
} else {
fireTreeStructureChanged(path);
}
return;
}
} finally {
ignoreSortedChange = isc;
}
}
}
int row = getRowForPath(path);
if (row < 0)
return;
if (e.getColumn() == TreeColumnModelEvent.ALL_COLUMNS) {
fireTableRowsUpdated(row, row);
} else {
fireTableCellUpdated(row, e.getColumn());
}
if (getRowHeight() <= 0) {
invalidatePath(e.getTreePath());
updateTableRowHeights(row, row+1);
}
}
// TODO, maybe add these?
// public void treeColumnAdded(TreeColumnModelEvent e);
// public void treeColumnRemoved(TreeColumnModelEvent e);
// TreeModelListener interface
@Override
public void treeNodesChanged(TreeModelEvent e) {
TreePath path = e.getTreePath();
int[] childIndices = e.getChildIndices();
Object[] childNodes = e.getChildren();
if (childNodes == null) {
fireTreeNodesChanged(path, childIndices, childNodes);
if (isRootVisible())
updateTable(0, 0, TableModelEvent.UPDATE);
} else {
processTreeNodesChanged(path, childIndices, childNodes);
updateTable(path, getRows(path, childNodes), TableModelEvent.UPDATE);
}
}
@Override
public void treeNodesInserted(TreeModelEvent e) {
TreePath path = e.getTreePath();
int[] childIndices = e.getChildIndices();
Object[] childNodes = e.getChildren();
if (childIndices == null || childNodes == null)
return;
// update the tree view first
processTreeNodesInserted(path, childIndices, childNodes);
// then update the table view
updateTable(path, getRows(path, childNodes), TableModelEvent.INSERT);
}
@Override
public void treeNodesRemoved(TreeModelEvent e) {
TreePath path = e.getTreePath();
int[] childIndices = e.getChildIndices();
Object[] childNodes = e.getChildren();
if (childIndices == null || childNodes == null)
return;
// find the row indices in the tree
int[] rows = getRows(path, childNodes);
// update the tree view first
processTreeNodesRemoved(path, childIndices, childNodes);
// then update the table view
updateTable(path, rows, TableModelEvent.DELETE);
}
private int[] getRows(TreePath parent, Object[] childNodes) {
if (!isExpanded(parent))
return null;
int row = getRowForPath(parent);
if (row < 0)
return null;
int[] rows = new int[childNodes.length];
int len = 0;
for (int i=0; i= 0)
rows[len++] = r;
}
if (len == 0)
return null;
if (len != rows.length)
rows = Arrays.copyOf(rows, len);
return rows;
}
@Override
public void treeStructureChanged(TreeModelEvent e) {
TreePath path = e.getTreePath();
boolean rootChanged = false;
if (path.getPathCount() == 1 && path.getLastPathComponent() != treeRoot) {
rootChanged = true;
treeRoot = path.getLastPathComponent();
}
// update the tree view
if (rowSorter != null)
rowSorter.structureChanged(path, rootChanged);
fireTreeStructureChanged(path);
// then update the table view
if (rootChanged) {
fireTableStructureChanged();
} else if (isExpanded(path)) {
fireTableDataChanged();
}
}
private void processTreeNodesChanged(TreePath path, int[] childIndices, Object[] childNodes) {
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(path.getLastPathComponent());
if (sorter != null) {
boolean isc = ignoreSortedChange;
ignoreSortedChange = true;
try {
if (childIndices.length == 1) {
int row = childIndices[0];
int viewRow = sorter.convertRowIndexToView(row);
sorter.rowsUpdated(row, row);
childIndices[0] = sorter.convertRowIndexToView(row);
if (childIndices[0] == viewRow) {
if (viewRow >= 0)
fireTreeNodesChanged(path, childIndices, childNodes);
return;
}
} else {
SorterHelper help = new SorterHelper(sorter, childIndices, childNodes);
int[] viewIndices = null;
Object[] viewNodes = null;
if (help.computeView()) {
viewIndices = help.viewIndices;
viewNodes = help.viewNodes;
}
sorter.rowsUpdated(childIndices[0], childIndices[childIndices.length-1]);
if (help.computeView()) {
if (Arrays.equals(viewIndices, help.viewIndices)
&& Arrays.equals(viewNodes, help.viewNodes)) {
fireTreeNodesChanged(path, viewIndices, viewNodes);
return;
}
} else if (viewIndices == null) {
return;
}
}
} finally {
ignoreSortedChange = isc;
}
updatePath(path, expandedDescendants(path));
return;
}
}
fireTreeNodesChanged(path, childIndices, childNodes);
}
private void processTreeNodesInserted(TreePath path, int[] childIndices, Object[] childNodes) {
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(path.getLastPathComponent());
if (sorter != null) {
boolean isc = ignoreSortedChange;
ignoreSortedChange = true;
try {
if (childIndices.length == 1) {
int row = childIndices[0];
sorter.rowsInserted(row, row);
childIndices[0] = sorter.convertRowIndexToView(row);
if (childIndices[0] >= 0)
fireTreeNodesInserted(path, childIndices, childNodes);
} else {
SorterHelper help = new SorterHelper(sorter, childIndices, childNodes);
if (help.useAllChanged()) {
sorter.allRowsChanged();
} else {
sorter.rowsInserted(help.firstRow, help.lastRow);
}
if (help.computeView())
fireTreeNodesInserted(path, help.viewIndices, help.viewNodes);
}
} finally {
ignoreSortedChange = isc;
}
return;
}
}
fireTreeNodesInserted(path, childIndices, childNodes);
}
private void processTreeNodesRemoved(TreePath path, int[] childIndices, Object[] childNodes) {
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(path.getLastPathComponent());
if (sorter != null) {
boolean isc = ignoreSortedChange;
ignoreSortedChange = true;
try {
if (childIndices.length == 1) {
int row = childIndices[0];
childIndices[0] = sorter.convertRowIndexToView(row);
if (childIndices[0] >= 0)
fireTreeNodesRemoved(path, childIndices, childNodes);
sorter.rowsDeleted(row, row);
} else {
SorterHelper help = new SorterHelper(sorter, childIndices, childNodes);
if (help.computeView())
fireTreeNodesRemoved(path, help.viewIndices, help.viewNodes);
if (help.useAllChanged()) {
sorter.allRowsChanged();
} else {
sorter.rowsDeleted(help.firstRow, help.lastRow);
}
}
} finally {
ignoreSortedChange = isc;
}
rowSorter.nodesRemoved(path, childNodes);
return;
}
}
fireTreeNodesRemoved(path, childIndices, childNodes);
}
private void updateTable(TreePath parent, int[] rows, int eventType) {
if (rows == null)
return;
int len = rows.length;
if (len == 1) {
updateTable(rows[0], rows[0], eventType);
} else {
Arrays.sort(rows, 0, len);
int firstRow, lastRow;
if (eventType == TableModelEvent.DELETE) {
firstRow = rows[len-1];
lastRow = rows[len-1];
for (int i=len-1; --i>=0;) {
int idx = rows[i];
if (idx == firstRow - 1) {
firstRow = idx;
} else {
updateTable(firstRow, lastRow, eventType);
firstRow = lastRow = idx;
}
}
} else {
firstRow = rows[0];
lastRow = rows[0];
for (int i=1; i=0; i-=2)
if (listeners[i]==ListSelectionListener.class)
((ListSelectionListener)listeners[i+1]).valueChanged(e);
}
// TreeModel Interface
@Override
public void addTreeModelListener(TreeModelListener l) {
listenerList.add(TreeModelListener.class, l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
listenerList.remove(TreeModelListener.class, l);
}
@Override
public int getChildCount(Object parent) {
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(parent);
if (sorter != null)
return sorter.getViewRowCount();
}
return treeModel.getChildCount(parent);
}
@Override
public Object getChild(Object parent, int index) {
return treeModel.getChild(parent, convertIndexToModel(parent, index));
}
@Override
public int getIndexOfChild(Object parent, Object child) {
int index = treeModel.getIndexOfChild(parent, child);
if (index < 0)
return index;
return convertIndexToView(parent, index);
}
@Override
public Object getRoot() {
return treeRoot;
}
@Override
public boolean isLeaf(Object node) {
return treeModel.isLeaf(node);
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
treeModel.valueForPathChanged(path, newValue);
}
public int convertIndexToModel(Object parent, int index) {
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(parent);
if (sorter != null)
return sorter.convertRowIndexToModel(index);
}
return index;
}
public int convertIndexToView(Object parent, int index) {
if (rowSorter != null) {
RowSorter> sorter = rowSorter.getRowSorter(parent);
if (sorter != null)
return sorter.convertRowIndexToView(index);
}
return index;
}
public void invalidateRows(int firstRow, int lastRow) {
if (firstRow == lastRow) {
invalidatePath(getPathForRow(firstRow));
} else {
HashMap> map = new HashMap>();
int row = firstRow;
TreePath path = getPathForRow(row);
TreePath parentPath = path.getParentPath();
if (parentPath == null) {
invalidatePaths(path, null, null);
path = getPathForRow(++row);
parentPath = path.getParentPath();
}
for (;;) {
ArrayList