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

Last change on this file since 5514 was 5514, checked in by stoecker, 12 years ago

allow to switch and remember display number for main window even in maximized mode

  • Property svn:eol-style set to native
File size: 14.2 KB
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 static public class WindowGeometryException extends Exception {
86 public WindowGeometryException(String message, Throwable cause) {
87 super(message, cause);
88 }
89
90 public WindowGeometryException(String message) {
91 super(message);
92 }
93 }
94
95 /** the top left point */
96 private Point topLeft;
97 /** the size */
98 private Dimension extent;
99
100 /**
101 * Creates a window geometry from a position and dimension
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 * Creates a window geometry from a rectangle
113 *
114 * @param rect the position
115 */
116 public WindowGeometry(Rectangle rect) {
117 this.topLeft = rect.getLocation();
118 this.extent = rect.getSize();
119 }
120
121 /**
122 * Creates a window geometry from the position and the size of a window.
123 *
124 * @param window the window
125 */
126 public WindowGeometry(Window window) {
127 this(window.getLocationOnScreen(), window.getSize());
128 }
129
130 /**
131 * Fixes a window geometry to shift to the correct screen.
132 *
133 * @param window the window
134 */
135 public void fixScreen(Window window) {
136 Rectangle oldScreen = getScreenInfo(getRectangle());
137 Rectangle newScreen = getScreenInfo(new Rectangle(window.getLocationOnScreen(), window.getSize()));
138 if(oldScreen.x != newScreen.x) {
139 this.topLeft.x += newScreen.x - oldScreen.x;
140 }
141 if(oldScreen.y != newScreen.y) {
142 this.topLeft.y += newScreen.y - oldScreen.y;
143 }
144 }
145
146 protected int parseField(String preferenceKey, String preferenceValue, String field) throws WindowGeometryException {
147 String v = "";
148 try {
149 Pattern p = Pattern.compile(field + "=(-?\\d+)",Pattern.CASE_INSENSITIVE);
150 Matcher m = p.matcher(preferenceValue);
151 if (!m.find())
152 throw new WindowGeometryException(tr("Preference with key ''{0}'' does not include ''{1}''. Cannot restore window geometry from preferences.", preferenceKey, field));
153 v = m.group(1);
154 return Integer.parseInt(v);
155 } catch(WindowGeometryException e) {
156 throw e;
157 } catch(NumberFormatException e) {
158 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));
159 } catch(Exception e) {
160 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);
161 }
162 }
163
164 protected void initFromPreferences(String preferenceKey) throws WindowGeometryException {
165 String value = Main.pref.get(preferenceKey);
166 if (value == null || value.equals(""))
167 throw new WindowGeometryException(tr("Preference with key ''{0}'' does not exist. Cannot restore window geometry from preferences.", preferenceKey));
168 topLeft = new Point();
169 extent = new Dimension();
170 topLeft.x = parseField(preferenceKey, value, "x");
171 topLeft.y = parseField(preferenceKey, value, "y");
172 extent.width = parseField(preferenceKey, value, "width");
173 extent.height = parseField(preferenceKey, value, "height");
174 }
175
176 protected void initFromWindowGeometry(WindowGeometry other) {
177 this.topLeft = other.topLeft;
178 this.extent = other.extent;
179 }
180
181 static public WindowGeometry mainWindow(String preferenceKey, String arg, boolean maximize) {
182 Rectangle screenDimension = getScreenInfo("gui.geometry");
183 if (arg != null) {
184 final Matcher m = Pattern.compile("(\\d+)x(\\d+)(([+-])(\\d+)([+-])(\\d+))?").matcher(arg);
185 if (m.matches()) {
186 int w = Integer.valueOf(m.group(1));
187 int h = Integer.valueOf(m.group(2));
188 int x = screenDimension.x, y = screenDimension.y;
189 if (m.group(3) != null) {
190 x = Integer.valueOf(m.group(5));
191 y = Integer.valueOf(m.group(7));
192 if (m.group(4).equals("-")) {
193 x = screenDimension.x + screenDimension.width - x - w;
194 }
195 if (m.group(6).equals("-")) {
196 y = screenDimension.y + screenDimension.height - y - h;
197 }
198 }
199 return new WindowGeometry(new Point(x,y), new Dimension(w,h));
200 } else {
201 Main.warn(tr("Ignoring malformed geometry: {0}", arg));
202 }
203 }
204 WindowGeometry def;
205 if(maximize) {
206 def = new WindowGeometry(screenDimension);
207 } else {
208 Point p = screenDimension.getLocation();
209 p.x += (screenDimension.width-1000)/2;
210 p.y += (screenDimension.height-740)/2;
211 def = new WindowGeometry(p, new Dimension(1000, 740));
212 }
213 return new WindowGeometry(preferenceKey, def);
214 }
215
216 /**
217 * Creates a window geometry from the values kept in the preference store under the
218 * key <code>preferenceKey</code>
219 *
220 * @param preferenceKey the preference key
221 * @throws WindowGeometryException thrown if no such key exist or if the preference value has
222 * an illegal format
223 */
224 public WindowGeometry(String preferenceKey) throws WindowGeometryException {
225 initFromPreferences(preferenceKey);
226 }
227
228 /**
229 * Creates a window geometry from the values kept in the preference store under the
230 * key <code>preferenceKey</code>. Falls back to the <code>defaultGeometry</code> if
231 * something goes wrong.
232 *
233 * @param preferenceKey the preference key
234 * @param defaultGeometry the default geometry
235 *
236 */
237 public WindowGeometry(String preferenceKey, WindowGeometry defaultGeometry) {
238 try {
239 initFromPreferences(preferenceKey);
240 } catch(WindowGeometryException e) {
241 initFromWindowGeometry(defaultGeometry);
242 }
243 }
244
245 /**
246 * Remembers a window geometry under a specific preference key
247 *
248 * @param preferenceKey the preference key
249 */
250 public void remember(String preferenceKey) {
251 StringBuffer value = new StringBuffer();
252 value.append("x=").append(topLeft.x).append(",")
253 .append("y=").append(topLeft.y).append(",")
254 .append("width=").append(extent.width).append(",")
255 .append("height=").append(extent.height);
256 Main.pref.put(preferenceKey, value.toString());
257 }
258
259 /**
260 * Replies the top left point for the geometry
261 *
262 * @return the top left point for the geometry
263 */
264 public Point getTopLeft() {
265 return topLeft;
266 }
267
268 /**
269 * Replies the size specified by the geometry
270 *
271 * @return the size specified by the geometry
272 */
273 public Dimension getSize() {
274 return extent;
275 }
276
277 /**
278 * Replies the size and position specified by the geometry
279 *
280 * @return the size and position specified by the geometry
281 */
282 private Rectangle getRectangle() {
283 return new Rectangle(topLeft, extent);
284 }
285
286 /**
287 * Applies this geometry to a window. Makes sure that the window is not
288 * placed outside of the coordinate range of all available screens.
289 *
290 * @param window the window
291 */
292 public void applySafe(Window window) {
293 Point p = new Point(topLeft);
294
295 Rectangle virtualBounds = new Rectangle();
296 GraphicsEnvironment ge = GraphicsEnvironment
297 .getLocalGraphicsEnvironment();
298 GraphicsDevice[] gs = ge.getScreenDevices();
299 for (int j = 0; j < gs.length; j++) {
300 GraphicsDevice gd = gs[j];
301 if (gd.getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
302 virtualBounds = virtualBounds.union(gd.getDefaultConfiguration().getBounds());
303 }
304 }
305
306 if (p.x < virtualBounds.x) {
307 p.x = virtualBounds.x;
308 } else if (p.x > virtualBounds.x + virtualBounds.width - extent.width) {
309 p.x = virtualBounds.x + virtualBounds.width - extent.width;
310 }
311
312 if (p.y < virtualBounds.y) {
313 p.y = virtualBounds.y;
314 } else if (p.y > virtualBounds.y + virtualBounds.height - extent.height) {
315 p.y = virtualBounds.y + virtualBounds.height - extent.height;
316 }
317
318 window.setLocation(p);
319 window.setSize(extent);
320 }
321
322 /**
323 * Find the size and position of the screen for given coordinates. Use first screen,
324 * when no coordinates are stored or null is passed.
325 *
326 * @param preferenceKey the key to get size and position from
327 * @return bounds of the screen
328 */
329 public static Rectangle getScreenInfo(String preferenceKey) {
330 Rectangle g = new WindowGeometry(preferenceKey,
331 /* default: something on screen 1 */
332 new WindowGeometry(new Point(0,0), new Dimension(10,10))).getRectangle();
333 return getScreenInfo(g);
334 }
335
336 /**
337 * Find the size and position of the screen for given coordinates. Use first screen,
338 * when no coordinates are stored or null is passed.
339 *
340 * @param g coordinates to check
341 * @return bounds of the screen
342 */
343 private static Rectangle getScreenInfo(Rectangle g) {
344 GraphicsEnvironment ge = GraphicsEnvironment
345 .getLocalGraphicsEnvironment();
346 GraphicsDevice[] gs = ge.getScreenDevices();
347 int intersect = 0;
348 Rectangle bounds = null;
349 for (int j = 0; j < gs.length; j++) {
350 GraphicsDevice gd = gs[j];
351 if (gd.getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
352 Rectangle b = gd.getDefaultConfiguration().getBounds();
353 if (b.height > 0 && b.width/b.height >= 3) /* multiscreen with wrong definition */
354 {
355 b.width /= 2;
356 Rectangle is = b.intersection(g);
357 int s = is.width*is.height;
358 if(bounds == null || intersect < s) {
359 intersect = s;
360 bounds = b;
361 }
362 b = new Rectangle(b);
363 b.x += b.width;
364 is = b.intersection(g);
365 s = is.width*is.height;
366 if(bounds == null || intersect < s) {
367 intersect = s;
368 bounds = b;
369 }
370 }
371 else
372 {
373 Rectangle is = b.intersection(g);
374 int s = is.width*is.height;
375 if(bounds == null || intersect < s) {
376 intersect = s;
377 bounds = b;
378 }
379 }
380 }
381 }
382 return bounds;
383 }
384
385 /**
386 * Find the size of the full virtual screen.
387 * @return size of the full virtual screen
388 */
389 public static Rectangle getFullScreenInfo() {
390 return new Rectangle(new Point(0,0), Toolkit.getDefaultToolkit().getScreenSize());
391 }
392
393 public String toString() {
394 return "WindowGeometry{topLeft="+topLeft+",extent="+extent+"}";
395 }
396}
Note: See TracBrowser for help on using the repository browser.