/*
* 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 .
*/
// Can be found at: https://code.google.com/archive/p/aephyr/source/default/source
// package aephyr.swing;
package be.nikiroo.utils.ui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import java.util.*;
public class TreeCellSpanner extends Container implements TreeCellRenderer, ComponentListener {
public TreeCellSpanner(JTree tree, TreeCellRenderer renderer) {
if (tree == null || renderer == null)
throw new NullPointerException();
this.tree = tree;
this.renderer = renderer;
treeParent = tree.getParent();
if (treeParent != null && treeParent instanceof JViewport) {
treeParent.addComponentListener(this);
} else {
treeParent = null;
tree.addComponentListener(this);
}
}
protected final JTree tree;
private TreeCellRenderer renderer;
private Component rendererComponent;
private Container treeParent;
private Map offsets = new HashMap();
private TreePath path;
public TreeCellRenderer getRenderer() {
return renderer;
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
path = tree.getPathForRow(row);
if (path != null && path.getLastPathComponent() != value)
path = null;
rendererComponent = renderer.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, hasFocus);
if (getComponentCount() < 1 || getComponent(0) != rendererComponent) {
removeAll();
add(rendererComponent);
}
return this;
}
@Override
public void doLayout() {
int x = getX();
if (x < 0)
return;
if (path != null) {
Integer offset = offsets.get(path);
if (offset == null || offset.intValue() != x) {
offsets.put(path, x);
fireTreePathChanged(path);
}
}
rendererComponent.setBounds(getX(), getY(), getWidth(), getHeight());
}
@Override
public void paint(Graphics g) {
if (rendererComponent != null)
rendererComponent.paint(g);
}
@Override
public Dimension getPreferredSize() {
Dimension s = rendererComponent.getPreferredSize();
// check if path count is greater than 1 to exclude the root
if (path != null && path.getPathCount() > 1) {
Integer offset = offsets.get(path);
if (offset != null) {
int width;
if (tree.getParent() == treeParent) {
width = treeParent.getWidth();
} else {
if (treeParent != null) {
treeParent.removeComponentListener(this);
tree.addComponentListener(this);
treeParent = null;
}
if (tree.getParent() instanceof JViewport) {
treeParent = tree.getParent();
tree.removeComponentListener(this);
treeParent.addComponentListener(this);
width = treeParent.getWidth();
} else {
width = tree.getWidth();
}
}
s.width = width - offset;
}
}
return s;
}
protected void fireTreePathChanged(TreePath path) {
if (path.getPathCount() > 1) {
// this cannot be used for the root node or else
// the entire tree will keep being revalidated ad infinitum
TreeModel model = tree.getModel();
Object node = path.getLastPathComponent();
if (node instanceof TreeNode && (model instanceof DefaultTreeModel
|| (model instanceof TreeModelTransformer> &&
(model=((TreeModelTransformer>)model).getModel()) instanceof DefaultTreeModel))) {
((DefaultTreeModel)model).nodeChanged((TreeNode)node);
} else {
model.valueForPathChanged(path, node.toString());
}
} else {
// root!
}
}
private int lastWidth;
@Override
public void componentHidden(ComponentEvent e) {}
@Override
public void componentMoved(ComponentEvent e) {}
@Override
public void componentResized(ComponentEvent e) {
if (e.getComponent().getWidth() != lastWidth) {
lastWidth = e.getComponent().getWidth();
for (int row=tree.getRowCount(); --row>=0;) {
fireTreePathChanged(tree.getPathForRow(row));
}
}
}
@Override
public void componentShown(ComponentEvent e) {}
}