source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/DialogsPanel.java@ 2869

Last change on this file since 2869 was 2869, checked in by jttt, 14 years ago

Removed ToggleDialog.tearDown(). All listeners should be unregistered in hideNotify()

  • Property svn:eol-style set to native
File size: 11.0 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.dialogs;
4
5import java.awt.Dimension;
6import java.util.ArrayList;
7import java.util.List;
8
9import javax.swing.BoxLayout;
10import javax.swing.JPanel;
11import javax.swing.JSplitPane;
12
13import org.openstreetmap.josm.gui.MultiSplitPane;
14import org.openstreetmap.josm.gui.MultiSplitLayout.Divider;
15import org.openstreetmap.josm.gui.MultiSplitLayout.Leaf;
16import org.openstreetmap.josm.gui.MultiSplitLayout.Node;
17import org.openstreetmap.josm.gui.MultiSplitLayout.Split;
18
19public class DialogsPanel extends JPanel {
20 protected List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
21 protected MultiSplitPane mSpltPane = new MultiSplitPane();
22 final protected int DIVIDER_SIZE = 5;
23
24 /**
25 * Panels that are added to the multisplitpane.
26 */
27 private List<JPanel> panels = new ArrayList<JPanel>();
28
29 final private JSplitPane parent;
30 public DialogsPanel(JSplitPane parent) {
31 this.parent = parent;
32 }
33
34 public boolean initialized = false; // read only from outside
35
36 public void initialize(List<ToggleDialog> pAllDialogs) {
37 if (initialized)
38 throw new IllegalStateException();
39 initialized = true;
40 allDialogs = new ArrayList<ToggleDialog>();
41
42 for (ToggleDialog dialog: pAllDialogs) {
43 add(dialog, false);
44 }
45
46 this.add(mSpltPane);
47 reconstruct(Action.ELEMENT_SHRINKS, null);
48 }
49
50 public void add(ToggleDialog dlg) {
51 add(dlg, true);
52 }
53
54 public void add(ToggleDialog dlg, boolean doReconstruct) {
55 allDialogs.add(dlg);
56 int i = allDialogs.size() - 1;
57 dlg.setDialogsPanel(this);
58 dlg.setVisible(false);
59 final JPanel p = new JPanel() {
60 /**
61 * Honoured by the MultiSplitPaneLayout when the
62 * entire Window is resized.
63 */
64 @Override
65 public Dimension getMinimumSize() {
66 return new Dimension(0, 40);
67 }
68 };
69 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
70 p.setVisible(false);
71
72 mSpltPane.add(p, "L"+i);
73 panels.add(p);
74
75 if (dlg.isDialogShowing()) {
76 dlg.showDialog();
77 if (dlg.isDialogInCollapsedView()) {
78 dlg.isCollapsed = false; // pretend to be in Default view, this will be set back by collapse()
79 dlg.collapse();
80 }
81 if (doReconstruct) {
82 reconstruct(Action.INVISIBLE_TO_DEFAULT, dlg);
83 }
84 } else {
85 dlg.hideDialog();
86 }
87 }
88
89 /**
90 * What action was performed to trigger the reconstruction
91 */
92 public enum Action {
93 INVISIBLE_TO_DEFAULT,
94 COLLAPSED_TO_DEFAULT,
95 /* INVISIBLE_TO_COLLAPSED, does not happen */
96 ELEMENT_SHRINKS /* else. (Remaining elements have more space.) */
97 }
98 /**
99 * Reconstruct the view, if the configurations of dialogs has changed.
100 * @param action what happened, so the reconstruction is necessary
101 * @param triggeredBy the dialog that caused the reconstruction
102 */
103 public void reconstruct(Action action, ToggleDialog triggeredBy) {
104
105 final int N = allDialogs.size();
106
107 /**
108 * reset the panels
109 */
110 for (JPanel p: panels) {
111 p.removeAll();
112 p.setVisible(false);
113 }
114
115 /**
116 * Add the elements to their respective panel.
117 *
118 * Each panel contains one dialog in default view and zero or more
119 * collapsed dialogs on top of it. The last panel is an exception
120 * as it can have collapsed dialogs at the bottom as well.
121 * If there are no dialogs in default view, show the collapsed ones
122 * in the last panel anyway.
123 */
124 int k = N-1; // index of the current Panel (start with last one)
125 JPanel p = panels.get(k); // current Panel
126 k = -1; // indicates that the current Panel index is N-1, but no default-view-Dialog was added to this Panel yet.
127 for (int i=N-1; i >= 0 ; --i) {
128 final ToggleDialog dlg = allDialogs.get(i);
129 if (dlg.isDialogInDefaultView()) {
130 if (k == -1) {
131 k = N-1;
132 } else {
133 --k;
134 p = panels.get(k);
135 }
136 p.add(dlg, 0);
137 p.setVisible(true);
138 }
139 else if (dlg.isDialogInCollapsedView()) {
140 p.add(dlg, 0);
141 p.setVisible(true);
142 }
143 }
144
145 if (k == -1) {
146 k = N-1;
147 }
148 final int numPanels = N - k;
149
150 /**
151 * Determine the panel geometry
152 */
153 if (action == Action.ELEMENT_SHRINKS) {
154 for (int i=0; i<N; ++i) {
155 final ToggleDialog dlg = allDialogs.get(i);
156 if (dlg.isDialogInDefaultView()) {
157 final int ph = dlg.getPreferredHeight();
158 final int ah = dlg.getSize().height;
159 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, (ah < 20 ? ph : ah)));
160 }
161 }
162 } else {
163 if (triggeredBy == null)
164 throw new IllegalArgumentException();
165
166 int sumP = 0; // sum of preferred heights of dialogs in default view (without the triggering dialog)
167 int sumA = 0; // sum of actual heights of dialogs in default view (without the triggering dialog)
168 int sumC = 0; // sum of heights of all collapsed dialogs (triggering dialog is never collapsed)
169
170 for (ToggleDialog dlg: allDialogs) {
171 if (dlg.isDialogInDefaultView()) {
172 if (dlg != triggeredBy) {
173 final int ph = dlg.getPreferredHeight();
174 final int ah = dlg.getHeight();
175 sumP += ph;
176 sumA += ah;
177 }
178 } else if (dlg.isDialogInCollapsedView()) {
179 sumC += dlg.getHeight();
180 }
181 }
182
183 /** total Height */
184 final int H = mSpltPane.getMultiSplitLayout().getModel().getBounds().getSize().height;
185
186 /** space, that is available for dialogs in default view (after the reconfiguration) */
187 final int s2 = H - (numPanels - 1) * DIVIDER_SIZE - sumC;
188
189 final int hp_trig = triggeredBy.getPreferredHeight();
190 if (hp_trig <= 0) throw new IllegalStateException(); // Must be positive
191
192 /** The new dialog gets a fair share */
193 final int hn_trig = hp_trig * s2 / (hp_trig + sumP);
194 triggeredBy.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn_trig));
195
196 /** This is remainig for the other default view dialogs */
197 final int R = s2 - hn_trig;
198
199 /**
200 * Take space only from dialogs that are relatively large
201 */
202 int D_m = 0; // additional space needed by the small dialogs
203 int D_p = 0; // available space from the large dialogs
204 for (int i=0; i<N; ++i) {
205 final ToggleDialog dlg = allDialogs.get(i);
206 if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
207 final int ha = dlg.getSize().height; // current
208 final int h0 = ha * R / sumA; // proportional shrinking
209 final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig); // fair share
210 if (h0 < he) { // dialog is relatively small
211 int hn = Math.min(ha, he); // shrink less, but do not grow
212 D_m += hn - h0;
213 } else { // dialog is relatively large
214 D_p += h0 - he;
215 }
216 }
217 }
218 /** adjust, without changing the sum */
219 for (int i=0; i<N; ++i) {
220 final ToggleDialog dlg = allDialogs.get(i);
221 if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
222 final int ha = dlg.getHeight();
223 final int h0 = ha * R / sumA;
224 final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig);
225 if (h0 < he) {
226 int hn = Math.min(ha, he);
227 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn));
228 } else {
229 int d;
230 try {
231 d = (h0-he) * D_m / D_p;
232 } catch (ArithmeticException e) { /* D_p may be zero - nothing wrong with that. */
233 d = 0;
234 }
235 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, h0 - d));
236 }
237 }
238 }
239 }
240
241 /**
242 * create Layout
243 */
244 final List<Node> ch = new ArrayList<Node>();
245
246 for (int i = k; i <= N-1; ++i) {
247 if (i != k) {
248 ch.add(new Divider());
249 }
250 Leaf l = new Leaf("L"+i);
251 l.setWeight(1.0 / numPanels);
252 ch.add(l);
253 }
254
255 if (numPanels == 1) {
256 Node model = ch.get(0);
257 mSpltPane.getMultiSplitLayout().setModel(model);
258 } else {
259 Split model = new Split();
260 model.setRowLayout(false);
261 model.setChildren(ch);
262 mSpltPane.getMultiSplitLayout().setModel(model);
263 }
264
265 mSpltPane.getMultiSplitLayout().setDividerSize(DIVIDER_SIZE);
266 mSpltPane.getMultiSplitLayout().setFloatingDividers(true);
267 mSpltPane.revalidate();
268
269 /**
270 * Hide the Panel, if there is nothing to show
271 */
272 if (numPanels == 1 && panels.get(N-1).getComponents().length == 0)
273 {
274 parent.setDividerSize(0);
275 this.setVisible(false);
276 } else {
277 if (this.getWidth() != 0) { // only if josm started with hidden panel
278 this.setPreferredSize(new Dimension(this.getWidth(), 0));
279 }
280 this.setVisible(true);
281 parent.setDividerSize(5);
282 parent.resetToPreferredSizes();
283 }
284 }
285
286 public void destroy() {
287 for (ToggleDialog t : allDialogs) {
288 t.destroy();
289 }
290 }
291
292 /**
293 * Replies the instance of a toggle dialog of type <code>type</code> managed by this
294 * map frame
295 *
296 * @param <T>
297 * @param type the class of the toggle dialog, i.e. UserListDialog.class
298 * @return the instance of a toggle dialog of type <code>type</code> managed by this
299 * map frame; null, if no such dialog exists
300 *
301 */
302 public <T> T getToggleDialog(Class<T> type) {
303 for (ToggleDialog td : allDialogs) {
304 if (type.isInstance(td))
305 return type.cast(td);
306 }
307 return null;
308 }
309}
Note: See TracBrowser for help on using the repository browser.