Commit | Line | Data |
---|---|---|
3cdf3fd8 NR |
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.fanfix_swing.gui.utils; | |
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 | } |