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

Last change on this file since 2475 was 2475, checked in by jttt, 16 years ago

Added methods showNotify and hideNotify to ToggleDialog. Useful to register listeners only when dialog is shown and expanded

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