001: /*
002: * @(#)PortingUtils.java 4/12/2006
003: *
004: * Copyright 2002 - 2006 JIDE Software Inc. All rights reserved.
005: */
006: package com.jidesoft.utils;
007:
008: import javax.swing.*;
009: import java.awt.*;
010: import java.awt.event.MouseEvent;
011: import java.awt.geom.Area;
012: import java.util.ArrayList;
013: import java.util.List;
014:
015: /**
016: * A class that keeps all 1.4/1.3 different stuff.
017: */
018: public class PortingUtils {
019: private static Rectangle _virtualBounds = null;
020:
021: /**
022: * Gets current focused components. If 1.3, just uses event's source;
023: * 1.4, used keyboard focus manager to get the correct focused component.
024: *
025: * @param event
026: * @return current focused component
027: */
028: public static Component getCurrentFocusComponent(AWTEvent event) {
029: return KeyboardFocusManager.getCurrentKeyboardFocusManager()
030: .getFocusOwner();
031: }
032:
033: /**
034: * Gets frame's state. In 1.3, used getState; in 1.4, uses getExtendedState.
035: *
036: * @param frame
037: * @return frame's state
038: */
039: public static int getFrameState(Frame frame) {
040: return frame.getExtendedState();
041: }
042:
043: /**
044: * Sets frame's state. In 1.3, uses sets frame's state; in 1.4, uses gets frame's state.
045: *
046: * @param frame
047: * @param state
048: */
049: public static void setFrameState(Frame frame, int state) {
050: frame.setExtendedState(state);
051: }
052:
053: /**
054: * Gets mouse modifiers. If 1.3, uses getModifiers; 1.4, getModifiersEx.
055: *
056: * @param e
057: * @return mouse modifiers
058: */
059: public static int getMouseModifiers(MouseEvent e) {
060: return e.getModifiersEx();
061: }
062:
063: /**
064: * Makes sure the component won't receive the focus.
065: *
066: * @param component
067: */
068: public static void removeFocus(JComponent component) {
069: component.setRequestFocusEnabled(false);
070: component.setFocusable(false);
071: }
072:
073: /**
074: * Removes the button border.
075: *
076: * @param button
077: */
078: public static void removeButtonBorder(AbstractButton button) {
079: button.setContentAreaFilled(false);
080: button.setMargin(new Insets(0, 0, 0, 0));
081: button.setBorder(BorderFactory.createEmptyBorder());
082: }
083:
084: /**
085: * To make sure the rectangle is within the screen bounds.
086: *
087: * @param invoker
088: * @param rect
089: * @return the rectange that is in the screen bounds.
090: */
091: public static Rectangle containsInScreenBounds(Component invoker,
092: Rectangle rect) {
093: Rectangle screenBounds = getScreenBounds(invoker);
094: Point p = rect.getLocation();
095: if (p.x + rect.width > screenBounds.x + screenBounds.width) {
096: p.x = screenBounds.x + screenBounds.width - rect.width;
097: }
098: if (p.y + rect.height > screenBounds.y + screenBounds.height) {
099: p.y = screenBounds.y + screenBounds.height - rect.height;
100: }
101: if (p.x < screenBounds.x) {
102: p.x = screenBounds.x;
103: }
104: if (p.y < screenBounds.y) {
105: p.y = screenBounds.y;
106: }
107: return new Rectangle(p, rect.getSize());
108: }
109:
110: /**
111: * To make sure the rectangle has overlap with the screen bounds.
112: *
113: * @param invoker
114: * @param rect
115: * @return the rectange that has overlap with the screen bounds.
116: */
117: public static Rectangle overlapWithScreenBounds(Component invoker,
118: Rectangle rect) {
119: Rectangle screenBounds = getScreenBounds(invoker);
120: Point p = rect.getLocation();
121: if (p.x > screenBounds.x + screenBounds.width) {
122: p.x = screenBounds.x + screenBounds.width - rect.width;
123: }
124: if (p.y > screenBounds.y + screenBounds.height) {
125: p.y = screenBounds.y + screenBounds.height - rect.height;
126: }
127: if (p.x + rect.width < screenBounds.x) {
128: p.x = screenBounds.x;
129: }
130: if (p.y + rect.height < screenBounds.y) {
131: p.y = screenBounds.y;
132: }
133: return new Rectangle(p, rect.getSize());
134: }
135:
136: /**
137: * Gets the screen size. In JDK1.4+, the returned size will exclude task bar area on Windows OS.
138: *
139: * @param invoker
140: * @return the screen size.
141: */
142: public static Dimension getScreenSize(Component invoker) {
143: ensureVirtualBounds();
144:
145: // to handle multi-display case
146: Dimension screenSize = _virtualBounds.getSize(); // Toolkit.getDefaultToolkit().getScreenSize();
147:
148: // jdk1.4 only
149: if (invoker != null && !(invoker instanceof JApplet)
150: && invoker.getGraphicsConfiguration() != null) {
151: Insets insets = Toolkit
152: .getDefaultToolkit()
153: .getScreenInsets(invoker.getGraphicsConfiguration());
154: screenSize.width -= insets.left + insets.right;
155: screenSize.height -= insets.top + insets.bottom;
156: }
157:
158: return screenSize;
159: }
160:
161: /**
162: * Gets the screen size. In JDK1.4+, the returned size will exclude task bar area on Windows OS.
163: *
164: * @param invoker
165: * @return the screen size.
166: */
167: public static Dimension getLocalScreenSize(Component invoker) {
168: ensureVirtualBounds();
169:
170: // jdk1.4 only
171: if (invoker != null && !(invoker instanceof JApplet)
172: && invoker.getGraphicsConfiguration() != null) {
173: // to handle multi-display case
174: GraphicsConfiguration gc = invoker
175: .getGraphicsConfiguration();
176: Rectangle bounds = gc.getBounds();
177: Insets insets = Toolkit.getDefaultToolkit()
178: .getScreenInsets(gc);
179: bounds.width -= insets.left + insets.right;
180: bounds.height -= insets.top + insets.bottom;
181: return bounds.getSize();
182: } else {
183: return getScreenSize(invoker);
184: }
185: }
186:
187: /**
188: * Gets the screen bounds. In JDK1.4+, the returned bounds will exclude task bar area on Windows OS.
189: *
190: * @param invoker
191: * @return the screen bounds.
192: */
193: public static Rectangle getScreenBounds(Component invoker) {
194: ensureVirtualBounds();
195:
196: // to handle multi-display case
197: Rectangle bounds = (Rectangle) _virtualBounds.clone();
198:
199: // TODO
200: // jdk1.4 only
201: if (invoker != null && !(invoker instanceof JApplet)
202: && invoker.getGraphicsConfiguration() != null) {
203: Insets insets = Toolkit
204: .getDefaultToolkit()
205: .getScreenInsets(invoker.getGraphicsConfiguration());
206: bounds.x += insets.left;
207: bounds.y += insets.top;
208: bounds.width -= insets.left + insets.right;
209: bounds.height -= insets.top + insets.bottom;
210: }
211:
212: return bounds;
213: }
214:
215: /**
216: * Gets the local monitor's screen bounds.
217: *
218: * @return the screen bounds.
219: */
220: public static Rectangle getLocalScreenBounds() {
221: GraphicsEnvironment e = GraphicsEnvironment
222: .getLocalGraphicsEnvironment();
223: return e.getMaximumWindowBounds();
224: }
225:
226: private static void ensureVirtualBounds() {
227: if (_virtualBounds == null) {
228: _virtualBounds = new Rectangle();
229: GraphicsEnvironment ge = GraphicsEnvironment
230: .getLocalGraphicsEnvironment();
231: GraphicsDevice[] gs = ge.getScreenDevices();
232: for (GraphicsDevice gd : gs) {
233: GraphicsConfiguration gc = gd.getDefaultConfiguration();
234: _virtualBounds = _virtualBounds.union(gc.getBounds());
235: }
236: }
237: }
238:
239: /**
240: * Makes the point parameter is within the screen bounds. If not, it will be modified to make sure it is in.
241: *
242: * @param invoker
243: * @param point
244: * @deprecated Please use {@link #ensureOnScreen(java.awt.Rectangle)} instead.
245: */
246: public static void withinScreen(Component invoker, Point point) {
247: if (invoker != null && !(invoker instanceof JApplet)) {
248: GraphicsConfiguration gc = invoker
249: .getGraphicsConfiguration();
250: Rectangle bounds = gc.getBounds();
251: Insets insets = Toolkit
252: .getDefaultToolkit()
253: .getScreenInsets(invoker.getGraphicsConfiguration());
254: if (point.x < bounds.x + insets.left) {
255: point.x = bounds.x + insets.left;
256: }
257: if (point.x > bounds.x + bounds.width - insets.right) {
258: point.x = bounds.x + bounds.width - insets.right;
259: }
260: if (point.y < bounds.y + insets.top) {
261: point.y = bounds.y + insets.top;
262: }
263: if (point.y > bounds.y + bounds.height - insets.bottom) {
264: point.y = bounds.y + bounds.height - insets.bottom;
265: }
266: }
267: }
268:
269: private static Area SCREEN_AREA;
270: private static Rectangle[] SCREENS;
271: private static Insets[] INSETS;
272:
273: private static Thread _initalizationThread = null;
274:
275: /**
276: * If you use methods such as {@link #ensureOnScreen(java.awt.Rectangle)}, {@link #getContainingScreenBounds(java.awt.Rectangle,boolean)}
277: * or {@link #getScreenArea()} for the first time, it will take up to a few seconds to run because it needs to get device information.
278: * To avoid any slowness, you can call {@link #initializeScreenArea()} method in the class where you will use those three methods.
279: * This method will spawn a thread to retrieve device information thus it will return immediately.
280: * Hopefully, when you use the three methods, the thread is done so user will not notice any slowness.
281: */
282: synchronized public static void initializeScreenArea() {
283: initializeScreenArea(Thread.NORM_PRIORITY);
284: }
285:
286: /**
287: * If you use methods such as {@link #ensureOnScreen(java.awt.Rectangle)}, {@link #getContainingScreenBounds(java.awt.Rectangle,boolean)}
288: * or {@link #getScreenArea()} for the first time, it will take up to a few seconds to run because it needs to get device information.
289: * To avoid any slowness, you can call {@link #initializeScreenArea()} method in the class where you will use those three methods.
290: * This method will spawn a thread to retrieve device information thus it will return immediately.
291: * Hopefully, when you use the three methods, the thread is done so user will not notice any slowness.
292: *
293: * @param priority as we will use a thread to calculate the screen area, you can use this parameter to control the priority of the thread. If you
294: * are waiting for the result before the next step, you should use normal priority (which is 5). If you just want to calcualte when app starts,
295: * you can use a lower priority (such as 3). For example, AbstractComboBox needs screen size so that the popup doesn't go beyond the screen.
296: * So when AbstractComboBox is used, we will kick off the thread at priority 3. If user clicks on the drop down after the thread finished,
297: * there will be no time delay.
298: */
299: synchronized public static void initializeScreenArea(int priority) {
300: if (_initalizationThread == null) {
301: _initalizationThread = new Thread() {
302: @Override
303: public void run() {
304: SCREEN_AREA = new Area();
305: GraphicsEnvironment environment = GraphicsEnvironment
306: .getLocalGraphicsEnvironment();
307: List screensList = new ArrayList();
308: List insetsList = new ArrayList();
309: GraphicsDevice[] screenDevices = environment
310: .getScreenDevices();
311: for (int i = 0; i < screenDevices.length; i++) {
312: GraphicsDevice device = screenDevices[i];
313: GraphicsConfiguration[] configurations = device
314: .getConfigurations();
315: for (int j = 0; j < configurations.length; j++) {
316: GraphicsConfiguration graphicsConfiguration = configurations[j];
317: Rectangle screenBounds = graphicsConfiguration
318: .getBounds();
319: Insets insets = Toolkit.getDefaultToolkit()
320: .getScreenInsets(
321: graphicsConfiguration);
322: screensList.add(screenBounds);
323: insetsList.add(insets);
324: SCREEN_AREA.add(new Area(screenBounds));
325: }
326: }
327: SCREENS = (Rectangle[]) screensList
328: .toArray(new Rectangle[screensList.size()]);
329: INSETS = (Insets[]) insetsList
330: .toArray(new Insets[screensList.size()]);
331: }
332: };
333: _initalizationThread.setPriority(priority);
334: _initalizationThread.start();
335: }
336: }
337:
338: public static boolean isInitalizationThreadAlive() {
339: return _initalizationThread != null
340: && _initalizationThread.isAlive();
341: }
342:
343: public static boolean isInitalizationThreadStarted() {
344: return _initalizationThread != null;
345: }
346:
347: private static void waitForInitialization() {
348: initializeScreenArea();
349:
350: while (_initalizationThread.isAlive()) {
351: try {
352: Thread.sleep(100);
353: } catch (InterruptedException e) {
354: }
355: }
356: }
357:
358: /**
359: * Ensures the rectangle is visible on the screen.
360: *
361: * @param invoker the invoking component
362: * @param bounds the input bounds
363: * @return the modified bounds.
364: */
365: public static Rectangle ensureVisible(Component invoker,
366: Rectangle bounds) {
367: Rectangle mainScreenBounds = PortingUtils
368: .getLocalScreenBounds(); // this is fast. Only if it is outside this bounds, we try the more expensive one.
369: if (!mainScreenBounds.contains(bounds.getLocation())) {
370: Rectangle screenBounds = PortingUtils
371: .getScreenBounds(invoker);
372: if (bounds.x > screenBounds.x + screenBounds.width
373: || bounds.x < screenBounds.x) {
374: bounds.x = mainScreenBounds.x;
375: }
376: if (bounds.y > screenBounds.y + screenBounds.height
377: || bounds.y < screenBounds.y) {
378: bounds.y = mainScreenBounds.y;
379: }
380: }
381: return bounds;
382: }
383:
384: /**
385: * Modifies the position of rect so that it is completly on screen if that is possible.
386: *
387: * @param rect The rectange to move onto a single screen
388: * @return rect after its position has been modified
389: */
390: public static Rectangle ensureOnScreen(Rectangle rect) {
391: // optimize it so that it is faster for most cases
392: Rectangle localScreenBounds = getLocalScreenBounds();
393: if (localScreenBounds.contains(rect)) {
394: return rect;
395: }
396:
397: waitForInitialization();
398:
399: // check if rect is totaly on screen
400: if (SCREEN_AREA.contains(rect))
401: return rect;
402: // see if the top left is on any of the screens
403: Rectangle containgScreen = null;
404: Point rectPos = rect.getLocation();
405: for (int i = 0; i < SCREENS.length; i++) {
406: Rectangle screenBounds = SCREENS[i];
407: if (screenBounds.contains(rectPos)) {
408: containgScreen = screenBounds;
409: break;
410: }
411: }
412: // if not see if rect partialy on any screen
413: for (int i = 0; i < SCREENS.length; i++) {
414: Rectangle screenBounds = SCREENS[i];
415: if (screenBounds.intersects(rect)) {
416: containgScreen = screenBounds;
417: break;
418: }
419: }
420: // check if it was on any screen
421: if (containgScreen == null) {
422: // it was not on any of the screens so center it on the first screen
423: rect.x = (SCREENS[0].width - rect.width) / 2;
424: rect.y = (SCREENS[0].width - rect.width) / 2;
425: return rect;
426: } else {
427: // move rect so it is completly on a single screen
428: // check X
429: int rectRight = rect.x + rect.width;
430: int screenRight = containgScreen.x + containgScreen.width;
431: if (rectRight > screenRight) {
432: rect.x = screenRight - rect.width;
433: }
434: if (rect.x < containgScreen.x)
435: rect.x = containgScreen.x;
436: // check Y
437: int rectBottom = rect.y + rect.height;
438: int screenBottom = containgScreen.y + containgScreen.height;
439: if (rectBottom > screenBottom) {
440: rect.y = screenBottom - rect.height;
441: }
442: if (rect.y < containgScreen.y)
443: rect.y = containgScreen.y;
444: // return corrected rect
445: return rect;
446: }
447: }
448:
449: /**
450: * Gets the screen bounds that contains the rect. The screen bounds consider the screen insets if any.
451: *
452: * @param rect
453: * @param considerInsets if consider the insets. The insets is for thing like Windows Task Bar.
454: * @return the screen bounds that contains the rect.
455: */
456: public static Rectangle getContainingScreenBounds(Rectangle rect,
457: boolean considerInsets) {
458: waitForInitialization();
459: // check if rect is totaly on screen
460: // if (SCREEN_AREA.contains(rect)) return SCREEN_AREA;
461:
462: // see if the top left is on any of the screens
463: Rectangle containgScreen = null;
464: Insets insets = null;
465: Point rectPos = rect.getLocation();
466: for (int i = 0; i < SCREENS.length; i++) {
467: Rectangle screenBounds = SCREENS[i];
468: if (screenBounds.contains(rectPos)) {
469: containgScreen = screenBounds;
470: insets = INSETS[i];
471: break;
472: }
473: }
474: // if not see if rect partialy on any screen
475: for (int i = 0; i < SCREENS.length; i++) {
476: Rectangle screenBounds = SCREENS[i];
477: if (screenBounds.intersects(rect)) {
478: containgScreen = screenBounds;
479: insets = INSETS[i];
480: break;
481: }
482: }
483:
484: // fall back to the first screen
485: if (containgScreen == null) {
486: containgScreen = SCREENS[0];
487: insets = INSETS[0];
488: }
489:
490: Rectangle bounds = new Rectangle(containgScreen);
491: if (considerInsets) {
492: bounds.x += insets.left;
493: bounds.y += insets.top;
494: bounds.width -= insets.left + insets.right;
495: bounds.height -= insets.top + insets.bottom;
496: }
497: return bounds;
498: }
499:
500: /**
501: * Get screen area of all monitors.
502: *
503: * @return Union of all screens
504: */
505: public static Area getScreenArea() {
506: waitForInitialization();
507: return SCREEN_AREA;
508: }
509:
510: /**
511: * Notifies user something is wrong. We use Toolkit beep method by default.
512: */
513: public static void notifyUser() {
514: Toolkit.getDefaultToolkit().beep();
515: }
516:
517: /**
518: * Checks the prerequisite needed by JIDE demos. If the prerequisite doesn't meet, it will prompt a message box and exit.
519: */
520: public static void prerequisiteChecking() {
521: if (!SystemInfo.isJdk14Above()) {
522: PortingUtils.notifyUser();
523: JOptionPane.showMessageDialog(null,
524: "J2SE 1.4 or above is required for this demo.",
525: "JIDE Software, Inc.", JOptionPane.WARNING_MESSAGE);
526: java.lang.System.exit(0);
527: }
528:
529: if (!SystemInfo.isJdk142Above()) {
530: PortingUtils.notifyUser();
531: JOptionPane
532: .showMessageDialog(
533: null,
534: "J2SE 1.4.2 or above is recommended for this demo for the best experience of seamless integration with Windows XP.",
535: "JIDE Software, Inc.",
536: JOptionPane.WARNING_MESSAGE);
537: }
538:
539: if (SystemInfo.isMacOSX()) { // set special properties for Mac OS X
540: java.lang.System.setProperty("apple.laf.useScreenMenuBar",
541: "true");
542: System.setProperty("apple.awt.brushMetalLook", "true");
543: }
544: }
545: }
|