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

Last change on this file since 2224 was 2224, checked in by stoecker, 15 years ago

see #3550 - patch by bastiK - allow resizing right handside dialogs, updated i18n

File size: 10.4 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.dialogs;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Dimension;
8
9import java.util.ArrayList;
10import java.util.List;
11
12import javax.swing.BoxLayout;
13import javax.swing.JPanel;
14
15import org.openstreetmap.josm.gui.MultiSplitLayout;
16import org.openstreetmap.josm.gui.MultiSplitLayout.Node;
17import org.openstreetmap.josm.gui.MultiSplitLayout.Leaf;
18import org.openstreetmap.josm.gui.MultiSplitLayout.Divider;
19import org.openstreetmap.josm.gui.MultiSplitLayout.Split;
20import org.openstreetmap.josm.gui.MultiSplitPane;
21import org.openstreetmap.josm.Main;
22
23public class DialogsPanel extends JPanel {
24 protected List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
25 protected MultiSplitPane mSpltPane = new MultiSplitPane();
26 final protected int DIVIDER_SIZE = 5;
27
28 /**
29 * Panels that are added to the multisplitpane.
30 */
31 private List<JPanel> panels = new ArrayList<JPanel>();
32
33 private boolean initialized = false;
34 public void initialize(List<ToggleDialog> allDialogs) {
35 if (initialized) {
36 throw new IllegalStateException();
37 }
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 public Dimension getMinimumSize() {
53 return new Dimension(0, 40);
54 }
55 };
56 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
57 p.setVisible(false);
58
59 mSpltPane.add(p, "L"+i);
60 panels.add(p);
61 }
62
63 for (Integer i=0; i < allDialogs.size(); ++i) {
64 final ToggleDialog dlg = allDialogs.get(i);
65 if (dlg.isDialogShowing()) {
66 dlg.showDialog();
67 if (dlg.isDialogInCollapsedView()) {
68 dlg.collapse();
69 }
70 } else {
71 dlg.hideDialog();
72 }
73 }
74 this.add(mSpltPane);
75 reconstruct(Action.ELEMENT_SHRINKS, null);
76 }
77
78 /**
79 * What action was performed to trigger the reconstruction
80 */
81 public enum Action {
82 INVISIBLE_TO_DEFAULT,
83 COLLAPSED_TO_DEFAULT,
84 /* INVISIBLE_TO_COLLAPSED, does not happen */
85 ELEMENT_SHRINKS /* else. (Remaining elements have more space.) */
86 };
87 /**
88 * Reconstruct the view, if the configurations of dialogs has changed.
89 * @param action what happened, so the reconstruction is necessary
90 * @param triggeredBy the dialog that caused the reconstruction
91 */
92 public void reconstruct(Action action, ToggleDialog triggeredBy) {
93
94 final int N = allDialogs.size();
95
96 /**
97 * reset the panels
98 */
99 for (int i=0; i < N; ++i) {
100 final JPanel p = panels.get(i);
101 p.removeAll();
102 p.setVisible(false);
103 }
104
105 /**
106 * Add the elements to their respective panel.
107 *
108 * Each panel contains one dialog in default view and zero or more
109 * collapsed dialogs on top of it. The last panel is an exception
110 * as it can have collapsed dialogs at the bottom as well.
111 * If there are no dialogs in default view, show the collapsed ones
112 * in the last panel anyway.
113 */
114 int k = N-1; // index of the current Panel (start with last one)
115 JPanel p = panels.get(k); // current Panel
116 k = -1; // indicates that the current Panel index is N-1, but no default-view-Dialog was added to this Panel yet.
117 for (int i=N-1; i >= 0 ; --i) {
118 final ToggleDialog dlg = allDialogs.get(i);
119 if (dlg.isDialogInDefaultView()) {
120 if (k == -1) {
121 k = N-1;
122 } else {
123 --k;
124 p = panels.get(k);
125 }
126 p.add(dlg, 0);
127 p.setVisible(true);
128 }
129 else if (dlg.isDialogInCollapsedView()) {
130 p.add(dlg, 0);
131 p.setVisible(true);
132 }
133 }
134
135 if (k == -1) {
136 k = N-1;
137 }
138 final int numPanels = N - k;
139
140 /**
141 * Determine the panel geometry
142 */
143 if (action == Action.ELEMENT_SHRINKS) {
144 for (int i=0; i<N; ++i) {
145 final ToggleDialog dlg = allDialogs.get(i);
146 if (dlg.isDialogInDefaultView()) {
147 final int ph = dlg.getPreferredHeight();
148 final int ah = dlg.getSize().height;
149 dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, (ah < 20 ? ph : ah)));
150 }
151 }
152 } else {
153 if (triggeredBy == null) {
154 throw new IllegalArgumentException();
155 }
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 public void destroy() {
264 for (ToggleDialog t : allDialogs) {
265 t.closeDetachedDialog();
266 }
267 }
268
269 /**
270 * Replies the instance of a toggle dialog of type <code>type</code> managed by this
271 * map frame
272 *
273 * @param <T>
274 * @param type the class of the toggle dialog, i.e. UserListDialog.class
275 * @return the instance of a toggle dialog of type <code>type</code> managed by this
276 * map frame; null, if no such dialog exists
277 *
278 */
279 public <T> T getToggleDialog(Class<T> type) {
280 for (ToggleDialog td : allDialogs) {
281 if (type.isInstance(td))
282 return type.cast(td);
283 }
284 return null;
285 }
286}
Note: See TracBrowser for help on using the repository browser.