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

Last change on this file since 2566 was 2566, checked in by bastiK, 16 years ago

Moved the code from agpifoj plugin to JOSM core. Thanks to Christian Gallioz for this great (ex-)plugin.
In the current state it might be a little unstable.

  • Did a view modification so it fits in better.
  • Added the Thumbnail feature, but still work to be done.

New in JOSM core: Possibility to add toggle dialogs not only on startup, but also later.

  • Property svn:eol-style set to native
File size: 11.2 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 (Integer i=0; i < pAllDialogs.size(); ++i) {
43 add(pAllDialogs.get(i), 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 (int i=0; i < N; ++i) {
111 final JPanel p = panels.get(i);
112 p.removeAll();
113 p.setVisible(false);
114 }
115
116 /**
117 * Add the elements to their respective panel.
118 *
119 * Each panel contains one dialog in default view and zero or more
120 * collapsed dialogs on top of it. The last panel is an exception
121 * as it can have collapsed dialogs at the bottom as well.
122 * If there are no dialogs in default view, show the collapsed ones
123 * in the last panel anyway.
124 */
125 int k = N-1; // index of the current Panel (start with last one)
126 JPanel p = panels.get(k); // current Panel
127 k = -1; // indicates that the current Panel index is N-1, but no default-view-Dialog was added to this Panel yet.
128 for (int i=N-1; i >= 0 ; --i) {
129 final ToggleDialog dlg = allDialogs.get(i);
130 if (dlg.isDialogInDefaultView()) {
131 if (k == -1) {
132 k = N-1;
133 } else {
134 --k;
135 p = panels.get(k);
136 }
137 p.add(dlg, 0);
138 p.setVisible(true);
139 }
140 else if (dlg.isDialogInCollapsedView()) {
141 p.add(dlg, 0);
142 p.setVisible(true);
143 }
144 }
145
146 if (k == -1) {
147 k = N-1;
148 }
149 final int numPanels = N - k;
150
151 /**
152 * Determine the panel geometry
153 */
154 if (action == Action.ELEMENT_SHRINKS) {
155 for (int i=0; i<N; ++i) {
156 final ToggleDialog dlg = allDialogs.get(i);
157 if (dlg.isDialogInDefaultView()) {
158 final int ph = dlg.getPreferredHeight();
159 final int ah = dlg.getSize().height;
160 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, (ah < 20 ? ph : ah)));
161 }
162 }
163 } else {
164 if (triggeredBy == null)
165 throw new IllegalArgumentException();
166
167 int sumP = 0; // sum of preferred heights of dialogs in default view (without the triggering dialog)
168 int sumA = 0; // sum of actual heights of dialogs in default view (without the triggering dialog)
169 int sumC = 0; // sum of heights of all collapsed dialogs (triggering dialog is never collapsed)
170
171 for (int i=0; i<N; ++i) {
172 final ToggleDialog dlg = allDialogs.get(i);
173 if (dlg.isDialogInDefaultView()) {
174 if (dlg != triggeredBy) {
175 final int ph = dlg.getPreferredHeight();
176 final int ah = dlg.getSize().height;
177 sumP += ph;
178 sumA += ah;
179 }
180 }
181 else if (dlg.isDialogInCollapsedView()) {
182 sumC += dlg.getSize().height;
183 }
184 }
185
186 /** total Height */
187 final int H = mSpltPane.getMultiSplitLayout().getModel().getBounds().getSize().height;
188
189 /** space, that is available for dialogs in default view (after the reconfiguration) */
190 final int s2 = H - (numPanels - 1) * DIVIDER_SIZE - sumC;
191
192 final int hp_trig = triggeredBy.getPreferredHeight();
193 if (hp_trig <= 0) throw new IllegalStateException(); // Must be positive
194
195 /** The new dialog gets a fair share */
196 final int hn_trig = hp_trig * s2 / (hp_trig + sumP);
197 triggeredBy.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn_trig));
198
199 /** This is remainig for the other default view dialogs */
200 final int R = s2 - hn_trig;
201
202 /**
203 * Take space only from dialogs that are relatively large
204 */
205 int D_m = 0; // additional space needed by the small dialogs
206 int D_p = 0; // available space from the large dialogs
207 for (int i=0; i<N; ++i) {
208 final ToggleDialog dlg = allDialogs.get(i);
209 if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
210 final int ha = dlg.getSize().height; // current
211 final int h0 = ha * R / sumA; // proportional shrinking
212 final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig); // fair share
213 if (h0 < he) { // dialog is relatively small
214 int hn = Math.min(ha, he); // shrink less, but do not grow
215 D_m += hn - h0;
216 } else { // dialog is relatively large
217 D_p += h0 - he;
218 }
219 }
220 }
221 /** adjust, without changing the sum */
222 for (int i=0; i<N; ++i) {
223 final ToggleDialog dlg = allDialogs.get(i);
224 if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
225 final int ha = dlg.getSize().height;
226 final int h0 = ha * R / sumA;
227 final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig);
228 if (h0 < he) {
229 int hn = Math.min(ha, he);
230 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn));
231 } else {
232 int d;
233 try {
234 d = (h0-he) * D_m / D_p;
235 } catch (ArithmeticException e) { /* D_p may be zero - nothing wrong with that. */
236 d = 0;
237 };
238 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, h0 - d));
239 }
240 }
241 }
242 }
243
244 /**
245 * create Layout
246 */
247 final List<Node> ch = new ArrayList<Node>();
248
249 for (int i = k; i <= N-1; ++i) {
250 if (i != k) {
251 ch.add(new Divider());
252 }
253 Leaf l = new Leaf("L"+i);
254 l.setWeight(1.0 / numPanels);
255 ch.add(l);
256 }
257
258 if (numPanels == 1) {
259 Node model = ch.get(0);
260 mSpltPane.getMultiSplitLayout().setModel(model);
261 } else {
262 Split model = new Split();
263 model.setRowLayout(false);
264 model.setChildren(ch);
265 mSpltPane.getMultiSplitLayout().setModel(model);
266 }
267
268 mSpltPane.getMultiSplitLayout().setDividerSize(DIVIDER_SIZE);
269 mSpltPane.getMultiSplitLayout().setFloatingDividers(true);
270 mSpltPane.revalidate();
271
272 /**
273 * Hide the Panel, if there is nothing to show
274 */
275 if (numPanels == 1 && panels.get(N-1).getComponents().length == 0)
276 {
277 parent.setDividerSize(0);
278 this.setVisible(false);
279 } else {
280 if (this.getWidth() != 0) { // only if josm started with hidden panel
281 this.setPreferredSize(new Dimension(this.getWidth(), 0));
282 }
283 this.setVisible(true);
284 parent.setDividerSize(5);
285 parent.resetToPreferredSizes();
286 }
287 }
288
289 public void destroy() {
290 for (ToggleDialog t : allDialogs) {
291 t.destroy();
292 }
293 }
294
295 /**
296 * Replies the instance of a toggle dialog of type <code>type</code> managed by this
297 * map frame
298 *
299 * @param <T>
300 * @param type the class of the toggle dialog, i.e. UserListDialog.class
301 * @return the instance of a toggle dialog of type <code>type</code> managed by this
302 * map frame; null, if no such dialog exists
303 *
304 */
305 public <T> T getToggleDialog(Class<T> type) {
306 for (ToggleDialog td : allDialogs) {
307 if (type.isInstance(td))
308 return type.cast(td);
309 }
310 return null;
311 }
312}
Note: See TracBrowser for help on using the repository browser.