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

Last change on this file since 2269 was 2269, checked in by stoecker, 15 years ago

fix #3674 - patch by bastiK - empty dialogs area did not vanish

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