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

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

sonar - fb-contrib:SEO_SUBOPTIMAL_EXPRESSION_ORDER - Performance - Method orders expressions in a conditional in a sub optimal way

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