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

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

fix #4030 - Hide/minimize icon in right menus not working properly

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