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

Last change on this file since 8345 was 8345, checked in by Don-vip, 9 years ago

code style - Useless parentheses around expressions should be removed to prevent any misunderstanding

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