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

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

sonar - Local variable and method parameter names should comply with a naming convention

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