small ui enhencements
[fanfix.git] / ui / TreeCellSpanner.java
1 /*
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as published
4 * by the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 */
15 // Can be found at: https://code.google.com/archive/p/aephyr/source/default/source
16 // package aephyr.swing;
17 package be.nikiroo.utils.ui;
18
19 import java.awt.*;
20 import java.awt.event.*;
21
22 import javax.swing.*;
23 import javax.swing.tree.*;
24
25 import java.util.*;
26
27 public class TreeCellSpanner extends Container implements TreeCellRenderer, ComponentListener {
28
29 public TreeCellSpanner(JTree tree, TreeCellRenderer renderer) {
30 if (tree == null || renderer == null)
31 throw new NullPointerException();
32 this.tree = tree;
33 this.renderer = renderer;
34 treeParent = tree.getParent();
35 if (treeParent != null && treeParent instanceof JViewport) {
36 treeParent.addComponentListener(this);
37 } else {
38 treeParent = null;
39 tree.addComponentListener(this);
40 }
41 }
42
43 protected final JTree tree;
44
45 private TreeCellRenderer renderer;
46
47 private Component rendererComponent;
48
49 private Container treeParent;
50
51 private Map<TreePath,Integer> offsets = new HashMap<TreePath,Integer>();
52
53 private TreePath path;
54
55 public TreeCellRenderer getRenderer() {
56 return renderer;
57 }
58
59 @Override
60 public Component getTreeCellRendererComponent(JTree tree, Object value,
61 boolean selected, boolean expanded, boolean leaf, int row,
62 boolean hasFocus) {
63 path = tree.getPathForRow(row);
64 if (path != null && path.getLastPathComponent() != value)
65 path = null;
66 rendererComponent = renderer.getTreeCellRendererComponent(
67 tree, value, selected, expanded, leaf, row, hasFocus);
68 if (getComponentCount() < 1 || getComponent(0) != rendererComponent) {
69 removeAll();
70 add(rendererComponent);
71 }
72 return this;
73 }
74
75 @Override
76 public void doLayout() {
77 int x = getX();
78 if (x < 0)
79 return;
80 if (path != null) {
81 Integer offset = offsets.get(path);
82 if (offset == null || offset.intValue() != x) {
83 offsets.put(path, x);
84 fireTreePathChanged(path);
85 }
86 }
87 rendererComponent.setBounds(getX(), getY(), getWidth(), getHeight());
88 }
89
90 @Override
91 public void paint(Graphics g) {
92 if (rendererComponent != null)
93 rendererComponent.paint(g);
94 }
95
96 @Override
97 public Dimension getPreferredSize() {
98 Dimension s = rendererComponent.getPreferredSize();
99 // check if path count is greater than 1 to exclude the root
100 if (path != null && path.getPathCount() > 1) {
101 Integer offset = offsets.get(path);
102 if (offset != null) {
103 int width;
104 if (tree.getParent() == treeParent) {
105 width = treeParent.getWidth();
106 } else {
107 if (treeParent != null) {
108 treeParent.removeComponentListener(this);
109 tree.addComponentListener(this);
110 treeParent = null;
111 }
112 if (tree.getParent() instanceof JViewport) {
113 treeParent = tree.getParent();
114 tree.removeComponentListener(this);
115 treeParent.addComponentListener(this);
116 width = treeParent.getWidth();
117 } else {
118 width = tree.getWidth();
119 }
120 }
121 s.width = width - offset;
122 }
123 }
124 return s;
125 }
126
127
128 protected void fireTreePathChanged(TreePath path) {
129 if (path.getPathCount() > 1) {
130 // this cannot be used for the root node or else
131 // the entire tree will keep being revalidated ad infinitum
132 TreeModel model = tree.getModel();
133 Object node = path.getLastPathComponent();
134 if (node instanceof TreeNode && (model instanceof DefaultTreeModel
135 || (model instanceof TreeModelTransformer<?> &&
136 (model=((TreeModelTransformer<?>)model).getModel()) instanceof DefaultTreeModel))) {
137 ((DefaultTreeModel)model).nodeChanged((TreeNode)node);
138 } else {
139 model.valueForPathChanged(path, node.toString());
140 }
141 } else {
142 // root!
143
144 }
145 }
146
147
148 private int lastWidth;
149
150 @Override
151 public void componentHidden(ComponentEvent e) {}
152
153 @Override
154 public void componentMoved(ComponentEvent e) {}
155
156 @Override
157 public void componentResized(ComponentEvent e) {
158 if (e.getComponent().getWidth() != lastWidth) {
159 lastWidth = e.getComponent().getWidth();
160 for (int row=tree.getRowCount(); --row>=0;) {
161 fireTreePathChanged(tree.getPathForRow(row));
162 }
163 }
164 }
165
166 @Override
167 public void componentShown(ComponentEvent e) {}
168
169 }