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

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

Sonar/FindBugs - various bugfixes / violation fixes

  • Property svn:eol-style set to native
File size: 11.3 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.MultiSplitLayout.Divider;
13import org.openstreetmap.josm.gui.MultiSplitLayout.Leaf;
14import org.openstreetmap.josm.gui.MultiSplitLayout.Node;
15import org.openstreetmap.josm.gui.MultiSplitLayout.Split;
16import org.openstreetmap.josm.gui.MultiSplitPane;
17import org.openstreetmap.josm.tools.Destroyable;
18
19public class DialogsPanel extends JPanel implements Destroyable {
20    protected List<ToggleDialog> allDialogs = new ArrayList<ToggleDialog>();
21    protected MultiSplitPane mSpltPane = new MultiSplitPane();
22    protected static final 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    public boolean initialized = false; // read only from outside
35
36    public void initialize(List<ToggleDialog> pAllDialogs) {
37        if (initialized)
38            throw new IllegalStateException();
39        initialized = true;
40        allDialogs = new ArrayList<ToggleDialog>();
41
42        for (ToggleDialog dialog: pAllDialogs) {
43            add(dialog, false);
44        }
45
46        this.add(mSpltPane);
47        reconstruct(Action.ELEMENT_SHRINKS, null);
48    }
49
50    public void add(ToggleDialog dlg) {
51        add(dlg, true);
52    }
53
54    public void add(ToggleDialog dlg, boolean doReconstruct) {
55        allDialogs.add(dlg);
56        int i = allDialogs.size() - 1;
57        dlg.setDialogsPanel(this);
58        dlg.setVisible(false);
59        final JPanel p = new JPanel() {
60            /**
61             * Honoured by the MultiSplitPaneLayout when the
62             * entire Window is resized.
63             */
64            @Override
65            public Dimension getMinimumSize() {
66                return new Dimension(0, 40);
67            }
68        };
69        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
70        p.setVisible(false);
71
72        mSpltPane.add(p, "L"+i);
73        panels.add(p);
74
75        if (dlg.isDialogShowing()) {
76            dlg.showDialog();
77            if (dlg.isDialogInCollapsedView()) {
78                dlg.isCollapsed = false;    // pretend to be in Default view, this will be set back by collapse()
79                dlg.collapse();
80            }
81            if (doReconstruct) {
82                reconstruct(Action.INVISIBLE_TO_DEFAULT, dlg);
83            }
84            dlg.showNotify();
85        } else {
86            dlg.hideDialog();
87        }
88    }
89
90    /**
91     * What action was performed to trigger the reconstruction
92     */
93    public enum Action {
94        INVISIBLE_TO_DEFAULT,
95        COLLAPSED_TO_DEFAULT,
96        /*  INVISIBLE_TO_COLLAPSED,    does not happen */
97        ELEMENT_SHRINKS         /* else. (Remaining elements have more space.) */
98    }
99    /**
100     * Reconstruct the view, if the configurations of dialogs has changed.
101     * @param action what happened, so the reconstruction is necessary
102     * @param triggeredBy the dialog that caused the reconstruction
103     */
104    public void reconstruct(Action action, ToggleDialog triggeredBy) {
105
106        final int N = allDialogs.size();
107
108        /**
109         * reset the panels
110         */
111        for (JPanel p: panels) {
112            p.removeAll();
113            p.setVisible(false);
114        }
115
116        /**
117         * Add the elements to their respective panel.
118         *
119         * Each panel contains one dialog in default view and zero or more
120         * collapsed dialogs on top of it. The last panel is an exception
121         * as it can have collapsed dialogs at the bottom as well.
122         * If there are no dialogs in default view, show the collapsed ones
123         * in the last panel anyway.
124         */
125        JPanel p = panels.get(N-1); // current Panel (start with last one)
126        int k = -1;                 // indicates that the current Panel index is N-1, but no default-view-Dialog has been added to this Panel yet.
127        for (int i=N-1; i >= 0 ; --i) {
128            final ToggleDialog dlg = allDialogs.get(i);
129            if (dlg.isDialogInDefaultView()) {
130                if (k == -1) {
131                    k = N-1;
132                } else {
133                    --k;
134                    p = panels.get(k);
135                }
136                p.add(dlg, 0);
137                p.setVisible(true);
138            }
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            if (triggeredBy == null)
164                throw new IllegalArgumentException();
165
166            int sumP = 0;   // sum of preferred heights of dialogs in default view (without the triggering dialog)
167            int sumA = 0;   // sum of actual heights of dialogs in default view (without the triggering dialog)
168            int sumC = 0;   // sum of heights of all collapsed dialogs (triggering dialog is never collapsed)
169
170            for (ToggleDialog dlg: allDialogs) {
171                if (dlg.isDialogInDefaultView()) {
172                    if (dlg != triggeredBy) {
173                        sumP += dlg.getPreferredHeight();
174                        sumA += dlg.getHeight();
175                    }
176                } else if (dlg.isDialogInCollapsedView()) {
177                    sumC += dlg.getHeight();
178                }
179            }
180
181            /**
182             * If we add additional dialogs on startup (e.g. geoimage), they may
183             * not have an actual height yet.
184             * In this case we simply reset everything to it's preferred size.
185             */
186            if (sumA == 0) {
187                reconstruct(Action.ELEMENT_SHRINKS, null);
188                return;
189            }
190
191            /** total Height */
192            final int H = mSpltPane.getMultiSplitLayout().getModel().getBounds().getSize().height;
193
194            /** space, that is available for dialogs in default view (after the reconfiguration) */
195            final int s2 = H - (numPanels - 1) * DIVIDER_SIZE - sumC;
196
197            final int hp_trig = triggeredBy.getPreferredHeight();
198            if (hp_trig <= 0) throw new IllegalStateException(); // Must be positive
199
200            /** The new dialog gets a fair share */
201            final int hn_trig = hp_trig * s2 / (hp_trig + sumP);
202            triggeredBy.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn_trig));
203
204            /** This is remainig for the other default view dialogs */
205            final int R = s2 - hn_trig;
206
207            /**
208             * Take space only from dialogs that are relatively large
209             */
210            int D_m = 0;        // additional space needed by the small dialogs
211            int D_p = 0;        // available space from the large dialogs
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;                              // current
216                    final int h0 = ha * R / sumA;                                     // proportional shrinking
217                    final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig);  // fair share
218                    if (h0 < he) {                  // dialog is relatively small
219                        int hn = Math.min(ha, he);  // shrink less, but do not grow
220                        D_m += hn - h0;
221                    } else {                        // dialog is relatively large
222                        D_p += h0 - he;
223                    }
224                }
225            }
226            /** adjust, without changing the sum */
227            for (int i=0; i<N; ++i) {
228                final ToggleDialog dlg = allDialogs.get(i);
229                if (dlg.isDialogInDefaultView() && dlg != triggeredBy) {
230                    final int ha = dlg.getHeight();
231                    final int h0 = ha * R / sumA;
232                    final int he = dlg.getPreferredHeight() * s2 / (sumP + hp_trig);
233                    if (h0 < he) {
234                        int hn = Math.min(ha, he);
235                        dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, hn));
236                    } else {
237                        int d;
238                        try {
239                            d = (h0-he) * D_m / D_p;
240                        } catch (ArithmeticException e) { /* D_p may be zero - nothing wrong with that. */
241                            d = 0;
242                        }
243                        dlg.setPreferredSize(new Dimension(Integer.MAX_VALUE, h0 - d));
244                    }
245                }
246            }
247        }
248
249        /**
250         * create Layout
251         */
252        final List<Node> ch = new ArrayList<Node>();
253
254        for (int i = k; i <= N-1; ++i) {
255            if (i != k) {
256                ch.add(new Divider());
257            }
258            Leaf l = new Leaf("L"+i);
259            l.setWeight(1.0 / numPanels);
260            ch.add(l);
261        }
262
263        if (numPanels == 1) {
264            Node model = ch.get(0);
265            mSpltPane.getMultiSplitLayout().setModel(model);
266        } else {
267            Split model = new Split();
268            model.setRowLayout(false);
269            model.setChildren(ch);
270            mSpltPane.getMultiSplitLayout().setModel(model);
271        }
272
273        mSpltPane.getMultiSplitLayout().setDividerSize(DIVIDER_SIZE);
274        mSpltPane.getMultiSplitLayout().setFloatingDividers(true);
275        mSpltPane.revalidate();
276
277        /**
278         * Hide the Panel, if there is nothing to show
279         */
280        if (numPanels == 1 && panels.get(N-1).getComponents().length == 0)
281        {
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>
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.