source: josm/trunk/src/org/openstreetmap/josm/tools/WindowGeometry.java @ 5241

Revision 5142, 13.0 KB checked in by stoecker, 7 weeks ago (diff)

fix multiscreen handling for unseparated virtual screens

  • Property svn:eol-style set to native
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Dimension;
8import java.awt.GraphicsConfiguration;
9import java.awt.GraphicsDevice;
10import java.awt.GraphicsEnvironment;
11import java.awt.Point;
12import java.awt.Rectangle;
13import java.awt.Toolkit;
14import java.awt.Window;
15import java.util.regex.Matcher;
16import java.util.regex.Pattern;
17
18import org.openstreetmap.josm.Main;
19
20/**
21 * This is a helper class for persisting the geometry of a JOSM window to the preference store
22 * and for restoring it from the preference store.
23 *
24 */
25public class WindowGeometry {
26
27    /**
28     * Replies a window geometry object for a window with a specific size which is
29     * centered on screen, where main window is
30     *
31     * @param extent  the size
32     * @return the geometry object
33     */
34    static public WindowGeometry centerOnScreen(Dimension extent) {
35        return centerOnScreen(extent, "gui.geometry");
36    }
37
38    /**
39     * Replies a window geometry object for a window with a specific size which is
40     * centered on screen where the corresponding window is.
41     *
42     * @param extent  the size
43     * @param preferenceKey the key to get window size and position from, null value format
44     * for whole virtual screen
45     * @return the geometry object
46     */
47    static public WindowGeometry centerOnScreen(Dimension extent, String preferenceKey) {
48        Rectangle size = preferenceKey != null ? getScreenInfo(preferenceKey)
49            : getFullScreenInfo();
50        Point topLeft = new Point(
51                size.x + Math.max(0, (size.width - extent.width) /2),
52                size.y + Math.max(0, (size.height - extent.height) /2)
53        );
54        return new WindowGeometry(topLeft, extent);
55    }
56
57    /**
58     * Replies a window geometry object for a window with a specific size which is centered
59     * relative to the parent window of a reference component.
60     *
61     * @param reference the reference component.
62     * @param extent the size
63     * @return the geometry object
64     */
65    static public WindowGeometry centerInWindow(Component reference, Dimension extent) {
66        Window parentWindow = null;
67        while(reference != null && ! (reference instanceof Window) ) {
68            reference = reference.getParent();
69        }
70        if (reference == null)
71            return new WindowGeometry(new Point(0,0), extent);
72        parentWindow = (Window)reference;
73        Point topLeft = new Point(
74                Math.max(0, (parentWindow.getSize().width - extent.width) /2),
75                Math.max(0, (parentWindow.getSize().height - extent.height) /2)
76        );
77        topLeft.x += parentWindow.getLocation().x;
78        topLeft.y += parentWindow.getLocation().y;
79        return new WindowGeometry(topLeft, extent);
80    }
81
82    /**
83     * Exception thrown by the WindowGeometry class if something goes wrong
84     *
85     */
86    static public class WindowGeometryException extends Exception {
87        public WindowGeometryException(String message, Throwable cause) {
88            super(message, cause);
89        }
90
91        public WindowGeometryException(String message) {
92            super(message);
93        }
94    }
95
96    /** the top left point */
97    private Point topLeft;
98    /** the size */
99    private Dimension extent;
100
101    /**
102     *
103     * @param topLeft the top left point
104     * @param extent the extent
105     */
106    public WindowGeometry(Point topLeft, Dimension extent) {
107        this.topLeft = topLeft;
108        this.extent = extent;
109    }
110
111    /**
112     *
113     * @param rect the position
114     */
115    public WindowGeometry(Rectangle rect) {
116        this.topLeft = rect.getLocation();
117        this.extent = rect.getSize();
118    }
119
120    /**
121     * Creates a window geometry from the position and the size of a window.
122     *
123     * @param window the window
124     */
125    public WindowGeometry(Window window)  {
126        this(window.getLocationOnScreen(), window.getSize());
127    }
128
129    protected int parseField(String preferenceKey, String preferenceValue, String field) throws WindowGeometryException {
130        String v = "";
131        try {
132            Pattern p = Pattern.compile(field + "=(-?\\d+)",Pattern.CASE_INSENSITIVE);
133            Matcher m = p.matcher(preferenceValue);
134            if (!m.find())
135                throw new WindowGeometryException(tr("Preference with key ''{0}'' does not include ''{1}''. Cannot restore window geometry from preferences.", preferenceKey, field));
136            v = m.group(1);
137            return Integer.parseInt(v);
138        } catch(WindowGeometryException e) {
139            throw e;
140        } catch(NumberFormatException e) {
141            throw new WindowGeometryException(tr("Preference with key ''{0}'' does not provide an int value for ''{1}''. Got {2}. Cannot restore window geometry from preferences.", preferenceKey, field, v));
142        } catch(Exception e) {
143            throw new WindowGeometryException(tr("Failed to parse field ''{1}'' in preference with key ''{0}''. Exception was: {2}. Cannot restore window geometry from preferences.", preferenceKey, field, e.toString()), e);
144        }
145    }
146
147    protected void initFromPreferences(String preferenceKey) throws WindowGeometryException {
148        String value = Main.pref.get(preferenceKey);
149        if (value == null || value.equals(""))
150            throw new WindowGeometryException(tr("Preference with key ''{0}'' does not exist. Cannot restore window geometry from preferences.", preferenceKey));
151        topLeft = new Point();
152        extent = new Dimension();
153        topLeft.x = parseField(preferenceKey, value, "x");
154        topLeft.y = parseField(preferenceKey, value, "y");
155        extent.width = parseField(preferenceKey, value, "width");
156        extent.height = parseField(preferenceKey, value, "height");
157    }
158
159    protected void initFromWindowGeometry(WindowGeometry other) {
160        this.topLeft = other.topLeft;
161        this.extent = other.extent;
162    }
163
164    static public WindowGeometry mainWindow(String preferenceKey, String arg, boolean maximize) {
165        Rectangle screenDimension = getScreenInfo("gui.geometry");
166        if (arg != null) {
167            final Matcher m = Pattern.compile("(\\d+)x(\\d+)(([+-])(\\d+)([+-])(\\d+))?").matcher(arg);
168            if (m.matches()) {
169                int w = Integer.valueOf(m.group(1));
170                int h = Integer.valueOf(m.group(2));
171                int x = screenDimension.x, y = screenDimension.y;
172                if (m.group(3) != null) {
173                    x = Integer.valueOf(m.group(5));
174                    y = Integer.valueOf(m.group(7));
175                    if (m.group(4).equals("-")) {
176                        x = screenDimension.x + screenDimension.width - x - w;
177                    }
178                    if (m.group(6).equals("-")) {
179                        y = screenDimension.y + screenDimension.height - y - h;
180                    }
181                }
182                return new WindowGeometry(new Point(x,y), new Dimension(w,h));
183            } else {
184                Main.warn(tr("Ignoring malformed geometry: {0}", arg));
185            }
186        }
187        WindowGeometry def;
188        if(maximize) {
189            def = new WindowGeometry(screenDimension);
190        } else {
191            Point p = screenDimension.getLocation();
192            p.x += (screenDimension.width-1000)/2;
193            p.y += (screenDimension.height-740)/2;
194            def = new WindowGeometry(p, new Dimension(1000, 740));
195        }
196        return new WindowGeometry(preferenceKey, def);
197    }
198
199    /**
200     * Creates a window geometry from the values kept in the preference store under the
201     * key <code>preferenceKey</code>
202     *
203     * @param preferenceKey the preference key
204     * @throws WindowGeometryException thrown if no such key exist or if the preference value has
205     * an illegal format
206     */
207    public WindowGeometry(String preferenceKey) throws WindowGeometryException {
208        initFromPreferences(preferenceKey);
209    }
210
211    /**
212     * Creates a window geometry from the values kept in the preference store under the
213     * key <code>preferenceKey</code>. Falls back to the <code>defaultGeometry</code> if
214     * something goes wrong.
215     *
216     * @param preferenceKey the preference key
217     * @param defaultGeometry the default geometry
218     *
219     */
220    public WindowGeometry(String preferenceKey, WindowGeometry defaultGeometry) {
221        try {
222            initFromPreferences(preferenceKey);
223        } catch(WindowGeometryException e) {
224            initFromWindowGeometry(defaultGeometry);
225        }
226    }
227
228    /**
229     * Remembers a window geometry under a specific preference key
230     *
231     * @param preferenceKey the preference key
232     */
233    public void remember(String preferenceKey) {
234        StringBuffer value = new StringBuffer();
235        value.append("x=").append(topLeft.x).append(",")
236        .append("y=").append(topLeft.y).append(",")
237        .append("width=").append(extent.width).append(",")
238        .append("height=").append(extent.height);
239        Main.pref.put(preferenceKey, value.toString());
240    }
241
242    /**
243     * Replies the top left point for the geometry
244     *
245     * @return  the top left point for the geometry
246     */
247    public Point getTopLeft() {
248        return topLeft;
249    }
250
251    /**
252     * Replies the size spezified by the geometry
253     *
254     * @return the size spezified by the geometry
255     */
256    public Dimension getSize() {
257        return extent;
258    }
259
260    private Rectangle getRectangle() {
261        return new Rectangle(topLeft, extent);
262    }
263
264    /**
265     * Applies this geometry to a window. Makes sure that the window is not
266     * placed outside of the coordinate range of all available screens.
267     *
268     * @param window the window
269     */
270    public void applySafe(Window window) {
271        Point p = new Point(topLeft);
272
273        Rectangle virtualBounds = new Rectangle();
274        GraphicsEnvironment ge = GraphicsEnvironment
275                .getLocalGraphicsEnvironment();
276        GraphicsDevice[] gs = ge.getScreenDevices();
277        for (int j = 0; j < gs.length; j++) {
278            GraphicsDevice gd = gs[j];
279            GraphicsConfiguration[] gc = gd.getConfigurations();
280            for (int i = 0; i < gc.length; i++) {
281                virtualBounds = virtualBounds.union(gc[i].getBounds());
282            }
283        }
284
285        if (p.x < virtualBounds.x) {
286            p.x = virtualBounds.x;
287        } else if (p.x > virtualBounds.x + virtualBounds.width - extent.width) {
288            p.x = virtualBounds.x + virtualBounds.width - extent.width;
289        }
290
291        if (p.y < virtualBounds.y) {
292            p.y = virtualBounds.y;
293        } else if (p.y > virtualBounds.y + virtualBounds.height - extent.height) {
294            p.y = virtualBounds.y + virtualBounds.height - extent.height;
295        }
296
297        window.setLocation(p);
298        window.setSize(extent);
299    }
300
301    /**
302     * Find the size and position of the screen for given coordinates. Use first screen,
303     * when no coordinates are stored or null is passed.
304     *
305     * @param preferenceKey the key to get size and position from
306     */
307    public static Rectangle getScreenInfo(String preferenceKey) {
308        Rectangle g = new WindowGeometry(preferenceKey,
309            /* default: something on screen 1 */
310            new WindowGeometry(new Point(0,0), new Dimension(10,10))).getRectangle();
311        GraphicsEnvironment ge = GraphicsEnvironment
312                .getLocalGraphicsEnvironment();
313        GraphicsDevice[] gs = ge.getScreenDevices();
314        int intersect = 0;
315        Rectangle bounds = null;
316        for (int j = 0; j < gs.length; j++) {
317            GraphicsDevice gd = gs[j];
318            GraphicsConfiguration[] gc = gd.getConfigurations();
319            for (int i = 0; i < gc.length; i++) {
320                Rectangle b = gc[i].getBounds();
321                if(b.width/b.height >= 3) /* multiscreen with wrong definition */
322                {
323                    b.width /= 2;
324                    Rectangle is = b.intersection(g);
325                    int s = is.width*is.height;
326                    if(bounds == null || intersect < s) {
327                        intersect = s;
328                        bounds = b;
329                    }
330                    b = new Rectangle(b);
331                    b.x += b.width;
332                    is = b.intersection(g);
333                    s = is.width*is.height;
334                    if(bounds == null || intersect < s) {
335                        intersect = s;
336                        bounds = b;
337                    }
338                }
339                else
340                {
341                    Rectangle is = b.intersection(g);
342                    int s = is.width*is.height;
343                    if(bounds == null || intersect < s) {
344                        intersect = s;
345                        bounds = b;
346                    }
347                }
348            }
349        }
350        return bounds;
351    }
352
353    /**
354     * Find the size of the full virtual screen.
355     */
356    public static Rectangle getFullScreenInfo() {
357        return new Rectangle(new Point(0,0), Toolkit.getDefaultToolkit().getScreenSize());
358    }
359
360    public String toString() {
361        return "WindowGeometry{topLeft="+topLeft+",extent="+extent+"}";
362    }
363}
Note: See TracBrowser for help on using the repository browser.