Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.stage;
027
028import java.util.List;
029import java.util.concurrent.atomic.AtomicBoolean;
030
031import javafx.collections.FXCollections;
032import javafx.collections.ObservableList;
033import javafx.geometry.Rectangle2D;
034
035import com.sun.javafx.tk.ScreenConfigurationAccessor;
036import com.sun.javafx.tk.TKScreenConfigurationListener;
037import com.sun.javafx.tk.Toolkit;
038
039/**
040 * Describes the characteristics of a graphics destination such as monitor.
041 * In a virtual device multi-screen environment in which the desktop area
042 * could span multiple physical screen devices, the bounds of the
043 * {@code Screen} objects are relative to the {@code Screen.primary}.
044 *
045 * <p>
046 * For example:
047 * <pre><code>
048 * Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
049 *
050 * //set Stage boundaries to visible bounds of the main screen
051 * stage.setX(primaryScreenBounds.getMinX());
052 * stage.setY(primaryScreenBounds.getMinY());
053 * stage.setWidth(primaryScreenBounds.getWidth());
054 * stage.setHeight(primaryScreenBounds.getHeight());
055 *
056 * stage.show();
057 * </code></pre>
058 * </p>
059 */
060public final class Screen {
061
062    private static final AtomicBoolean configurationDirty =
063            new AtomicBoolean(true);
064
065    private static final ScreenConfigurationAccessor accessor;
066
067    private static Screen primary;
068    private static final ObservableList<Screen> screens =
069            FXCollections.<Screen>observableArrayList();
070    private static final ObservableList<Screen> unmodifiableScreens =
071            FXCollections.unmodifiableObservableList(screens);
072
073    static {
074        accessor = Toolkit.getToolkit().setScreenConfigurationListener(new TKScreenConfigurationListener() {
075            @Override public void screenConfigurationChanged() {
076                updateConfiguration();
077            }
078        });
079    }
080
081    private Screen() {
082    }
083
084    private static void checkDirty() {
085        if (configurationDirty.compareAndSet(true, false)) {
086            updateConfiguration();
087        }
088    }
089
090    private static void updateConfiguration() {
091        Object primaryScreen = Toolkit.getToolkit().getPrimaryScreen();
092        Screen screenTmp = nativeToScreen(primaryScreen, Screen.primary);
093        if (screenTmp != null) {
094            Screen.primary = screenTmp;
095        }
096
097        List<?> screens = Toolkit.getToolkit().getScreens();
098        // go through the list of new screens, see if they match the
099        // existing list; if they do reuse the list; if they don't
100        // at least try to reuse some of the old ones
101        ObservableList<Screen> newScreens = FXCollections.<Screen>observableArrayList();
102        // if the size of the new and the old one are different just
103        // recreate the list
104        boolean canKeepOld = (Screen.screens.size() == screens.size());
105        for (int i = 0; i < screens.size(); i++) {
106            Object obj = screens.get(i);
107            Screen origScreen = null;
108            if (canKeepOld) {
109                origScreen = Screen.screens.get(i);
110            }
111            Screen newScreen = nativeToScreen(obj, origScreen);
112            if (newScreen != null) {
113                if (canKeepOld) {
114                    canKeepOld = false;
115                    newScreens.clear();
116                    newScreens.addAll(Screen.screens.subList(0, i));
117                }
118                newScreens.add(newScreen);
119            }
120        }
121        if (!canKeepOld) {
122            Screen.screens.clear();
123            Screen.screens.addAll(newScreens);
124        }
125
126        configurationDirty.set(false);
127    }
128
129    // returns null if the new one is to be equal the old one
130    private static Screen nativeToScreen(Object obj, Screen screen) {
131        int minX = accessor.getMinX(obj);
132        int minY = accessor.getMinY(obj);
133        int width = accessor.getWidth(obj);
134        int height = accessor.getHeight(obj);
135        int visualMinX = accessor.getVisualMinX(obj);
136        int visualMinY = accessor.getVisualMinY(obj);
137        int visualWidth = accessor.getVisualWidth(obj);
138        int visualHeight = accessor.getVisualHeight(obj);
139        double dpi = accessor.getDPI(obj);
140        if ((screen == null) ||
141            (screen.bounds.getMinX() != minX) ||
142            (screen.bounds.getMinY() != minY) ||
143            (screen.bounds.getWidth() != width) ||
144            (screen.bounds.getHeight() != height) ||
145            (screen.visualBounds.getMinX() != visualMinX) ||
146            (screen.visualBounds.getMinY() != visualMinY) ||
147            (screen.visualBounds.getWidth() != visualWidth) ||
148            (screen.visualBounds.getHeight() != visualHeight) ||
149            (screen.dpi != dpi))
150        {
151            Screen s = new Screen();
152            s.bounds = new Rectangle2D(minX, minY, width, height);
153            s.visualBounds = new Rectangle2D(visualMinX, visualMinY, visualWidth, visualHeight);
154            s.dpi = dpi;
155            return s;
156        } else {
157            return null;
158        }
159    }
160
161    /**
162     * The primary {@code Screen}.
163     */
164    public static Screen getPrimary() {
165        checkDirty();
166        return primary;
167    }
168
169    /**
170      * The observable list of currently available {@code Screens}.
171      */
172    public static ObservableList<Screen> getScreens() {
173        checkDirty();
174        return unmodifiableScreens;
175    }
176
177    /**
178      * Returns a ObservableList of {@code Screens} that intersects the provided rectangle.
179      *
180      * @param x the x coordinate of the upper-left corner of the specified
181      *   rectangular area
182      * @param y the y coordinate of the upper-left corner of the specified
183      *   rectangular area
184      * @param width the width of the specified rectangular area
185      * @param height the height of the specified rectangular area
186      * @return a ObservableList of {@code Screens} for which {@code Screen.bounds}
187      *   intersects the provided rectangle
188      */
189    public static ObservableList<Screen> getScreensForRectangle(
190            double x, double y, double width, double height)
191    {
192        checkDirty();
193        ObservableList<Screen> results = FXCollections.<Screen>observableArrayList();
194        for (Screen screen : screens) {
195            if (screen.bounds.intersects(x, y, width, height)) {
196                results.add(screen);
197            }
198        }
199        return results;
200    }
201
202    /**
203      * Returns a ObservableList of {@code Screens} that intersects the provided rectangle.
204      *
205      * @param r The specified {@code Rectangle2D}
206      * @return a ObservableList of {@code Screens} for which {@code Screen.bounds}
207      *   intersects the provided rectangle
208      */
209    public static ObservableList<Screen> getScreensForRectangle(Rectangle2D r) {
210        checkDirty();
211        return getScreensForRectangle(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
212    }
213
214    /**
215     * The bounds of this {@code Screen}.
216     */
217    private Rectangle2D bounds = Rectangle2D.EMPTY;
218    /**
219     * Gets the bounds of this {@code Screen}.
220     * @return The bounds of this {@code Screen}
221     */
222    public final Rectangle2D getBounds() {
223        return bounds;
224    }
225
226    /**
227     * The visual bounds of this {@code Screen}.
228     * 
229     * These bounds account for objects in the native windowing system such as
230     * task bars and menu bars. These bounds are contained by {@code Screen.bounds}.
231     */
232    private Rectangle2D visualBounds = Rectangle2D.EMPTY;
233    /**
234     * Gets the visual bounds of this {@code Screen}.
235     * 
236     * These bounds account for objects in the native windowing system such as
237     * task bars and menu bars. These bounds are contained by {@code Screen.bounds}.
238     * @return The visual bounds of this {@code Screen}
239     */
240    public final Rectangle2D getVisualBounds() {
241        return visualBounds;
242    }
243
244    /**
245      * The resolution (dots per inch) of this {@code Screen}.
246      */
247    private double dpi;
248    /**
249     * Gets the resolution (dots per inch) of this {@code Screen}.
250     * @return The resolution of this @{code Screen}
251     */
252    public final double getDpi() {
253        return dpi;
254    }
255
256    /**
257     * Returns a hash code for this {@code Screen} object.
258     * @return a hash code for this {@code Screen} object.
259     */ 
260    @Override public int hashCode() {
261        long bits = 7L;
262        bits = 37L * bits + bounds.hashCode();
263        bits = 37L * bits + visualBounds.hashCode();
264        bits = 37L * bits + Double.doubleToLongBits(dpi);
265        return (int) (bits ^ (bits >> 32));
266    }
267
268    /**
269     * Indicates whether some other object is "equal to" this one.
270     * @param obj the reference object with which to compare.
271     * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
272     */
273    @Override public boolean equals(Object obj) {
274        if (obj == this) return true;
275        if (obj instanceof Screen) {
276            Screen other = (Screen) obj;
277            return (bounds == null ? other.bounds == null : bounds.equals(other.bounds))
278              && (visualBounds == null ? other.visualBounds == null : visualBounds.equals(other.visualBounds))
279              && other.dpi == dpi;
280        } else return false;
281    }
282
283    /**
284     * Returns a string representation of this {@code Screen} object.
285     * @return a string representation of this {@code Screen} object.
286     */ 
287    @Override public String toString() {
288        return super.toString() + "bounds:" + bounds + " visualBounds:" + visualBounds + " dpi:" + dpi;
289    }
290}