VAqua.src.org.violetlib.treetable.TreeTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaqua Show documentation
Show all versions of vaqua Show documentation
An improved native Swing look and feel for macOS
The newest version!
/*
* 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 org.violetlib.treetable;
import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.*;
import javax.swing.*;
import javax.swing.RowSorter.SortKey;
import javax.swing.border.Border;
import javax.swing.event.*;
import javax.swing.plaf.UIResource;
import javax.swing.table.*;
import javax.swing.text.Position;
import javax.swing.tree.*;
import org.violetlib.aqua.AquaUtils;
import org.violetlib.treetable.event.*;
import org.violetlib.treetable.ui.BasicTreeTableUI;
import org.violetlib.treetable.ui.TableInterface;
import org.violetlib.treetable.ui.TreeInterface;
import org.violetlib.treetable.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() {
return AquaUtils.getScrollPaneAncestor(this);
}
// 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.setTreeModel(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 setTreeModel(TreeModel m) {
if (treeModel != null) {
treeModel.removeTreeModelListener(this);
}
treeModel = m;
if (treeModel != null) {
treeModel.addTreeModelListener(this);
TreeModelEvent e = new TreeModelEvent(treeModel, new TreePath(treeModel.getRoot()));
treeStructureChanged(e);
}
}
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 = path.getParentPath() != null ? rowSorter.getRowSorter(path.getParentPath().getLastPathComponent()): null;
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;
// parent could be the invisible tree root
if (parent.getPathCount() > 1 && getRowForPath(parent) < 0) {
return null;
}
int[] rows = new int[childNodes.length];
int len = 0;
for (Object childNode : childNodes) {
TreePath path = parent.pathByAddingChild(childNode);
int r = getRowForPath(path);
if (r >= 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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy