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

Last change on this file since 2613 was 2613, checked in by Gubaer, 16 years ago

new: global in-memory cache for downloaded changesets
new: toggle dialog for changesets
new: downloading of changesets (currently without changeset content, will follow later)

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