/*
* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.util;
import static com.sun.javafx.FXPermissions.ACCESS_WINDOW_LIST_PERMISSION;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.HPos;
import javafx.geometry.NodeOrientation;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.Stop;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.util.List;
import com.sun.javafx.PlatformUtil;
import java.security.AccessController;
import java.security.PrivilegedAction;
import com.sun.glass.utils.NativeLibLoader;
import com.sun.prism.impl.PrismSettings;
Some basic utilities which need to be in java (for shifting operations or
other reasons), which are not toolkit dependent.
/**
* Some basic utilities which need to be in java (for shifting operations or
* other reasons), which are not toolkit dependent.
*
*/
public class Utils {
/***************************************************************************
* *
* Math-related utilities *
* *
**************************************************************************/
Simple utility function which clamps the given value to be strictly
between the min and max values.
/**
* Simple utility function which clamps the given value to be strictly
* between the min and max values.
*/
public static float clamp(float min, float value, float max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
Simple utility function which clamps the given value to be strictly
between the min and max values.
/**
* Simple utility function which clamps the given value to be strictly
* between the min and max values.
*/
public static int clamp(int min, int value, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
Simple utility function which clamps the given value to be strictly
between the min and max values.
/**
* Simple utility function which clamps the given value to be strictly
* between the min and max values.
*/
public static double clamp(double min, double value, double max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
Simple utility function which clamps the given value to be strictly
above the min value.
/**
* Simple utility function which clamps the given value to be strictly
* above the min value.
*/
public static double clampMin(double value, double min) {
if (value < min) return min;
return value;
}
Simple utility function which clamps the given value to be strictly
under the max value.
/**
* Simple utility function which clamps the given value to be strictly
* under the max value.
*/
public static int clampMax(int value, int max) {
if (value > max) return max;
return value;
}
Utility function which returns either less
or more
depending on which value
is closer to. If value
is perfectly between them, then either may be returned. /**
* Utility function which returns either {@code less} or {@code more}
* depending on which {@code value} is closer to. If {@code value}
* is perfectly between them, then either may be returned.
*/
public static double nearest(double less, double value, double more) {
double lessDiff = value - less;
double moreDiff = more - value;
if (lessDiff < moreDiff) return less;
return more;
}
/***************************************************************************
* *
* String-related utilities *
* *
**************************************************************************/
Helper to remove leading and trailing quotes from a string.
Works with single or double quotes.
/**
* Helper to remove leading and trailing quotes from a string.
* Works with single or double quotes.
*/
public static String stripQuotes(String str) {
if (str == null) return str;
if (str.length() == 0) return str;
int beginIndex = 0;
final char openQuote = str.charAt(beginIndex);
if ( openQuote == '\"' || openQuote=='\'' ) beginIndex += 1;
int endIndex = str.length();
final char closeQuote = str.charAt(endIndex - 1);
if ( closeQuote == '\"' || closeQuote=='\'' ) endIndex -= 1;
if ((endIndex - beginIndex) < 0) return str;
// note that String.substring returns "this" if beginIndex == 0 && endIndex == count
// or a new string that shares the character buffer with the original string.
return str.substring(beginIndex, endIndex);
}
Because mobile doesn't have string.split(s) function, this function
was written.
/**
* Because mobile doesn't have string.split(s) function, this function
* was written.
*/
public static String[] split(String str, String separator) {
if (str == null || str.length() == 0) return new String[] { };
if (separator == null || separator.length() == 0) return new String[] { };
if (separator.length() > str.length()) return new String[] { };
java.util.List<String> result = new java.util.ArrayList<String>();
int index = str.indexOf(separator);
while (index >= 0) {
String newStr = str.substring(0, index);
if (newStr != null && newStr.length() > 0) {
result.add(newStr);
}
str = str.substring(index + separator.length());
index = str.indexOf(separator);
}
if (str != null && str.length() > 0) {
result.add(str);
}
return result.toArray(new String[] { });
}
Because mobile doesn't have string.contains(s) function, this function
was written.
/**
* Because mobile doesn't have string.contains(s) function, this function
* was written.
*/
public static boolean contains(String src, String s) {
if (src == null || src.length() == 0) return false;
if (s == null || s.length() == 0) return false;
if (s.length() > src.length()) return false;
return src.indexOf(s) > -1;
}
/***************************************************************************
* *
* Color-related utilities *
* *
**************************************************************************/
Calculates a perceptual brightness for a color between 0.0 black and 1.0 while
/**
* Calculates a perceptual brightness for a color between 0.0 black and 1.0 while
*/
public static double calculateBrightness(Color color) {
return (0.3*color.getRed()) + (0.59*color.getGreen()) + (0.11*color.getBlue());
}
Derives a lighter or darker of a given color.
Params: - c – The color to derive from
- brightness – The brightness difference for the new color -1.0 being 100% dark which is always black, 0.0 being
no change and 1.0 being 100% lighter which is always white
/**
* Derives a lighter or darker of a given color.
*
* @param c The color to derive from
* @param brightness The brightness difference for the new color -1.0 being 100% dark which is always black, 0.0 being
* no change and 1.0 being 100% lighter which is always white
*/
public static Color deriveColor(Color c, double brightness) {
double baseBrightness = calculateBrightness(c);
double calcBrightness = brightness;
// Fine adjustments to colors in ranges of brightness to adjust the contrast for them
if (brightness > 0) {
if (baseBrightness > 0.85) {
calcBrightness = calcBrightness * 1.6;
} else if (baseBrightness > 0.6) {
// no change
} else if (baseBrightness > 0.5) {
calcBrightness = calcBrightness * 0.9;
} else if (baseBrightness > 0.4) {
calcBrightness = calcBrightness * 0.8;
} else if (baseBrightness > 0.3) {
calcBrightness = calcBrightness * 0.7;
} else {
calcBrightness = calcBrightness * 0.6;
}
} else {
if (baseBrightness < 0.2) {
calcBrightness = calcBrightness * 0.6;
}
}
// clamp brightness
if (calcBrightness < -1) { calcBrightness = -1; } else if (calcBrightness > 1) {calcBrightness = 1;}
// window two take the calculated brightness multiplyer and derive color based on source color
double[] hsb = RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue());
// change brightness
if (calcBrightness > 0) { // brighter
hsb[1] *= 1 - calcBrightness;
hsb[2] += (1 - hsb[2]) * calcBrightness;
} else { // darker
hsb[2] *= calcBrightness + 1;
}
// clip saturation and brightness
if (hsb[1] < 0) { hsb[1] = 0;} else if (hsb[1] > 1) {hsb[1] = 1;}
if (hsb[2] < 0) { hsb[2] = 0;} else if (hsb[2] > 1) {hsb[2] = 1;}
// convert back to color
Color c2 = Color.hsb((int)hsb[0], hsb[1], hsb[2],c.getOpacity());
return Color.hsb((int)hsb[0], hsb[1], hsb[2],c.getOpacity());
/* var hsb:Number[] = RGBtoHSB(c.red,c.green,c.blue);
// change brightness
if (brightness > 0) {
//var bright:Number = brightness * (1-calculateBrightness(c));
var bright:Number = if (calculateBrightness(c)<0.65 and brightness > 0.5) {
if (calculateBrightness(c)<0.2) then brightness * 0.55 else brightness * 0.7
} else brightness;
// brighter
hsb[1] *= 1 - bright;
hsb[2] += (1 - hsb[2]) * bright;
} else {
// darker
hsb[2] *= brightness+1;
}
// clip saturation and brightness
if (hsb[1] < 0) { hsb[1] = 0;} else if (hsb[1] > 1) {hsb[1] = 1}
if (hsb[2] < 0) { hsb[2] = 0;} else if (hsb[2] > 1) {hsb[2] = 1}
// convert back to color
return Color.hsb(hsb[0],hsb[1],hsb[2]) */
}
interpolate at a set position
between two colors color1
and color2
. The interpolation is done is linear RGB color space not the default sRGB color space. /**
* interpolate at a set {@code position} between two colors {@code color1} and {@code color2}.
* The interpolation is done is linear RGB color space not the default sRGB color space.
*/
private static Color interpolateLinear(double position, Color color1, Color color2) {
Color c1Linear = convertSRGBtoLinearRGB(color1);
Color c2Linear = convertSRGBtoLinearRGB(color2);
return convertLinearRGBtoSRGB(Color.color(
c1Linear.getRed() + (c2Linear.getRed() - c1Linear.getRed()) * position,
c1Linear.getGreen() + (c2Linear.getGreen() - c1Linear.getGreen()) * position,
c1Linear.getBlue() + (c2Linear.getBlue() - c1Linear.getBlue()) * position,
c1Linear.getOpacity() + (c2Linear.getOpacity() - c1Linear.getOpacity()) * position
));
}
Get the color at the give position
in the ladder of color stops /**
* Get the color at the give {@code position} in the ladder of color stops
*/
private static Color ladder(final double position, final Stop[] stops) {
Stop prevStop = null;
for (int i=0; i<stops.length; i++) {
Stop stop = stops[i];
if(position <= stop.getOffset()){
if (prevStop == null) {
return stop.getColor();
} else {
return interpolateLinear((position-prevStop.getOffset())/(stop.getOffset()-prevStop.getOffset()), prevStop.getColor(), stop.getColor());
}
}
prevStop = stop;
}
// position is greater than biggest stop, so will we biggest stop's color
return prevStop.getColor();
}
Get the color at the give position
in the ladder of color stops /**
* Get the color at the give {@code position} in the ladder of color stops
*/
public static Color ladder(final Color color, final Stop[] stops) {
return ladder(calculateBrightness(color), stops);
}
public static double[] HSBtoRGB(double hue, double saturation, double brightness) {
// normalize the hue
double normalizedHue = ((hue % 360) + 360) % 360;
hue = normalizedHue/360;
double r = 0, g = 0, b = 0;
if (saturation == 0) {
r = g = b = brightness;
} else {
double h = (hue - Math.floor(hue)) * 6.0;
double f = h - java.lang.Math.floor(h);
double p = brightness * (1.0 - saturation);
double q = brightness * (1.0 - saturation * f);
double t = brightness * (1.0 - (saturation * (1.0 - f)));
switch ((int) h) {
case 0:
r = brightness;
g = t;
b = p;
break;
case 1:
r = q;
g = brightness;
b = p;
break;
case 2:
r = p;
g = brightness;
b = t;
break;
case 3:
r = p;
g = q;
b = brightness;
break;
case 4:
r = t;
g = p;
b = brightness;
break;
case 5:
r = brightness;
g = p;
b = q;
break;
}
}
double[] f = new double[3];
f[0] = r;
f[1] = g;
f[2] = b;
return f;
}
public static double[] RGBtoHSB(double r, double g, double b) {
double hue, saturation, brightness;
double[] hsbvals = new double[3];
double cmax = (r > g) ? r : g;
if (b > cmax) cmax = b;
double cmin = (r < g) ? r : g;
if (b < cmin) cmin = b;
brightness = cmax;
if (cmax != 0)
saturation = (double) (cmax - cmin) / cmax;
else
saturation = 0;
if (saturation == 0) {
hue = 0;
} else {
double redc = (cmax - r) / (cmax - cmin);
double greenc = (cmax - g) / (cmax - cmin);
double bluec = (cmax - b) / (cmax - cmin);
if (r == cmax)
hue = bluec - greenc;
else if (g == cmax)
hue = 2.0 + redc - bluec;
else
hue = 4.0 + greenc - redc;
hue = hue / 6.0;
if (hue < 0)
hue = hue + 1.0;
}
hsbvals[0] = hue * 360;
hsbvals[1] = saturation;
hsbvals[2] = brightness;
return hsbvals;
}
Helper function to convert a color in sRGB space to linear RGB space.
/**
* Helper function to convert a color in sRGB space to linear RGB space.
*/
public static Color convertSRGBtoLinearRGB(Color color) {
double[] colors = new double[] { color.getRed(), color.getGreen(), color.getBlue() };
for (int i=0; i<colors.length; i++) {
if (colors[i] <= 0.04045) {
colors[i] = colors[i] / 12.92;
} else {
colors[i] = Math.pow((colors[i] + 0.055) / 1.055, 2.4);
}
}
return Color.color(colors[0], colors[1], colors[2], color.getOpacity());
}
Helper function to convert a color in linear RGB space to SRGB space.
/**
* Helper function to convert a color in linear RGB space to SRGB space.
*/
public static Color convertLinearRGBtoSRGB(Color color) {
double[] colors = new double[] { color.getRed(), color.getGreen(), color.getBlue() };
for (int i=0; i<colors.length; i++) {
if (colors[i] <= 0.0031308) {
colors[i] = colors[i] * 12.92;
} else {
colors[i] = (1.055 * Math.pow(colors[i], (1.0 / 2.4))) - 0.055;
}
}
return Color.color(colors[0], colors[1], colors[2], color.getOpacity());
}
helper function for calculating the sum of a series of numbers /** helper function for calculating the sum of a series of numbers */
public static double sum(double[] values) {
double sum = 0;
for (double v : values) sum = sum+v;
return sum / values.length;
}
public static Point2D pointRelativeTo(Node parent, Node node, HPos hpos,
VPos vpos, double dx, double dy, boolean reposition)
{
final double nodeWidth = node.getLayoutBounds().getWidth();
final double nodeHeight = node.getLayoutBounds().getHeight();
return pointRelativeTo(parent, nodeWidth, nodeHeight, hpos, vpos, dx, dy, reposition);
}
public static Point2D pointRelativeTo(Node parent, double anchorWidth,
double anchorHeight, HPos hpos, VPos vpos, double dx, double dy,
boolean reposition)
{
final Bounds parentBounds = getBounds(parent);
Scene scene = parent.getScene();
NodeOrientation orientation = parent.getEffectiveNodeOrientation();
if (orientation == NodeOrientation.RIGHT_TO_LEFT) {
if (hpos == HPos.LEFT) {
hpos = HPos.RIGHT;
} else if (hpos == HPos.RIGHT) {
hpos = HPos.LEFT;
}
dx *= -1;
}
double layoutX = positionX(parentBounds, anchorWidth, hpos) + dx;
final double layoutY = positionY(parentBounds, anchorHeight, vpos) + dy;
if (orientation == NodeOrientation.RIGHT_TO_LEFT && hpos == HPos.CENTER) {
//TODO - testing for an instance of Stage seems wrong but works for menus
if (scene.getWindow() instanceof Stage) {
layoutX = layoutX + parentBounds.getWidth() - anchorWidth;
} else {
layoutX = layoutX - parentBounds.getWidth() - anchorWidth;
}
}
if (reposition) {
return pointRelativeTo(parent, anchorWidth, anchorHeight, layoutX, layoutY, hpos, vpos);
} else {
return new Point2D(layoutX, layoutY);
}
}
This is the fallthrough function that most other functions fall into. It takes
care specifically of the repositioning of the item such that it remains onscreen
as best it can, given it's unique qualities.
As will all other functions, this one returns a Point2D that represents an x,y
location that should safely position the item onscreen as best as possible.
Note that width
and refer to the width and height of the
node/popup that is needing to be repositioned, not of the parent.
Don't use the BASELINE vpos, it doesn't make sense and would produce wrong result.
/**
* This is the fallthrough function that most other functions fall into. It takes
* care specifically of the repositioning of the item such that it remains onscreen
* as best it can, given it's unique qualities.
*
* As will all other functions, this one returns a Point2D that represents an x,y
* location that should safely position the item onscreen as best as possible.
*
* Note that <code>width</code> and <height> refer to the width and height of the
* node/popup that is needing to be repositioned, not of the parent.
*
* Don't use the BASELINE vpos, it doesn't make sense and would produce wrong result.
*/
public static Point2D pointRelativeTo(Object parent, double width,
double height, double screenX, double screenY, HPos hpos, VPos vpos)
{
double finalScreenX = screenX;
double finalScreenY = screenY;
final Bounds parentBounds = getBounds(parent);
// ...and then we get the bounds of this screen
final Screen currentScreen = getScreen(parent);
final Rectangle2D screenBounds =
hasFullScreenStage(currentScreen)
? currentScreen.getBounds()
: currentScreen.getVisualBounds();
// test if this layout will force the node to appear outside
// of the screens bounds. If so, we must reposition the item to a better position.
// We firstly try to do this intelligently, so as to not overlap the parent if
// at all possible.
if (hpos != null) {
// Firstly we consider going off the right hand side
if ((finalScreenX + width) > screenBounds.getMaxX()) {
finalScreenX = positionX(parentBounds, width, getHPosOpposite(hpos, vpos));
}
// don't let the node go off to the left of the current screen
if (finalScreenX < screenBounds.getMinX()) {
finalScreenX = positionX(parentBounds, width, getHPosOpposite(hpos, vpos));
}
}
if (vpos != null) {
// don't let the node go off the bottom of the current screen
if ((finalScreenY + height) > screenBounds.getMaxY()) {
finalScreenY = positionY(parentBounds, height, getVPosOpposite(hpos,vpos));
}
// don't let the node out of the top of the current screen
if (finalScreenY < screenBounds.getMinY()) {
finalScreenY = positionY(parentBounds, height, getVPosOpposite(hpos,vpos));
}
}
// --- after all the moving around, we do one last check / rearrange.
// Unlike the check above, this time we are just fully committed to keeping
// the item on screen at all costs, regardless of whether or not that results
/// in overlapping the parent object.
if ((finalScreenX + width) > screenBounds.getMaxX()) {
finalScreenX -= (finalScreenX + width - screenBounds.getMaxX());
}
if (finalScreenX < screenBounds.getMinX()) {
finalScreenX = screenBounds.getMinX();
}
if ((finalScreenY + height) > screenBounds.getMaxY()) {
finalScreenY -= (finalScreenY + height - screenBounds.getMaxY());
}
if (finalScreenY < screenBounds.getMinY()) {
finalScreenY = screenBounds.getMinY();
}
return new Point2D(finalScreenX, finalScreenY);
}
Utility function that returns the x-axis position that an object should be positioned at,
given the parents screen bounds, the width of the object, and
the required HPos.
/**
* Utility function that returns the x-axis position that an object should be positioned at,
* given the parents screen bounds, the width of the object, and
* the required HPos.
*/
private static double positionX(Bounds parentBounds, double width, HPos hpos) {
if (hpos == HPos.CENTER) {
// this isn't right, but it is needed for root menus to show properly
return parentBounds.getMinX();
} else if (hpos == HPos.RIGHT) {
return parentBounds.getMaxX();
} else if (hpos == HPos.LEFT) {
return parentBounds.getMinX() - width;
} else {
return 0;
}
}
Utility function that returns the y-axis position that an object should be positioned at,
given the parents screen bounds, the height of the object, and
the required VPos.
The BASELINE vpos doesn't make sense here, 0 is returned for it.
/**
* Utility function that returns the y-axis position that an object should be positioned at,
* given the parents screen bounds, the height of the object, and
* the required VPos.
*
* The BASELINE vpos doesn't make sense here, 0 is returned for it.
*/
private static double positionY(Bounds parentBounds, double height, VPos vpos) {
if (vpos == VPos.BOTTOM) {
return parentBounds.getMaxY();
} else if (vpos == VPos.CENTER) {
return parentBounds.getMinY();
} else if (vpos == VPos.TOP) {
return parentBounds.getMinY() - height;
} else {
return 0;
}
}
To facilitate multiple types of parent object, we unfortunately must allow for
Objects to be passed in. This method handles determining the bounds of the
given Object. If the Object type is not supported, a default Bounds will be returned.
/**
* To facilitate multiple types of parent object, we unfortunately must allow for
* Objects to be passed in. This method handles determining the bounds of the
* given Object. If the Object type is not supported, a default Bounds will be returned.
*/
private static Bounds getBounds(Object obj) {
if (obj instanceof Node) {
final Node n = (Node)obj;
Bounds b = n.localToScreen(n.getLayoutBounds());
return b != null ? b : new BoundingBox(0, 0, 0, 0);
} else if (obj instanceof Window) {
final Window window = (Window)obj;
return new BoundingBox(window.getX(), window.getY(), window.getWidth(), window.getHeight());
} else {
return new BoundingBox(0, 0, 0, 0);
}
}
/*
* Simple utitilty function to return the 'opposite' value of a given HPos, taking
* into account the current VPos value. This is used to try and avoid overlapping.
*/
private static HPos getHPosOpposite(HPos hpos, VPos vpos) {
if (vpos == VPos.CENTER) {
if (hpos == HPos.LEFT){
return HPos.RIGHT;
} else if (hpos == HPos.RIGHT){
return HPos.LEFT;
} else if (hpos == HPos.CENTER){
return HPos.CENTER;
} else {
// by default center for now
return HPos.CENTER;
}
} else {
return HPos.CENTER;
}
}
/*
* Simple utitilty function to return the 'opposite' value of a given VPos, taking
* into account the current HPos value. This is used to try and avoid overlapping.
*/
private static VPos getVPosOpposite(HPos hpos, VPos vpos) {
if (hpos == HPos.CENTER) {
if (vpos == VPos.BASELINE){
return VPos.BASELINE;
} else if (vpos == VPos.BOTTOM){
return VPos.TOP;
} else if (vpos == VPos.CENTER){
return VPos.CENTER;
} else if (vpos == VPos.TOP){
return VPos.BOTTOM;
} else {
// by default center for now
return VPos.CENTER;
}
} else {
return VPos.CENTER;
}
}
public static boolean hasFullScreenStage(final Screen screen) {
final List<Window> allWindows = AccessController.doPrivileged(
(PrivilegedAction<List<Window>>) () -> Window.getWindows(),
null,
ACCESS_WINDOW_LIST_PERMISSION);
for (final Window window : allWindows) {
if (window instanceof Stage) {
final Stage stage = (Stage) window;
if (stage.isFullScreen() && (getScreen(stage) == screen)) {
return true;
}
}
}
return false;
}
/*
* Returns true if the primary Screen has QVGA dimensions, in landscape or portrait mode.
*/
public static boolean isQVGAScreen() {
Rectangle2D bounds = Screen.getPrimary().getBounds();
return ((bounds.getWidth() == 320 && bounds.getHeight() == 240) ||
(bounds.getWidth() == 240 && bounds.getHeight() == 320));
}
This function attempts to determine the best screen given the parent object
from which we are wanting to position another item relative to. This is particularly
important when we want to keep items from going off screen, and for handling
multiple monitor support.
/**
* This function attempts to determine the best screen given the parent object
* from which we are wanting to position another item relative to. This is particularly
* important when we want to keep items from going off screen, and for handling
* multiple monitor support.
*/
public static Screen getScreen(Object obj) {
final Bounds parentBounds = getBounds(obj);
final Rectangle2D rect = new Rectangle2D(
parentBounds.getMinX(),
parentBounds.getMinY(),
parentBounds.getWidth(),
parentBounds.getHeight());
return getScreenForRectangle(rect);
}
public static Screen getScreenForRectangle(final Rectangle2D rect) {
final List<Screen> screens = Screen.getScreens();
final double rectX0 = rect.getMinX();
final double rectX1 = rect.getMaxX();
final double rectY0 = rect.getMinY();
final double rectY1 = rect.getMaxY();
Screen selectedScreen;
selectedScreen = null;
double maxIntersection = 0;
for (final Screen screen: screens) {
final Rectangle2D screenBounds = screen.getBounds();
final double intersection =
getIntersectionLength(rectX0, rectX1,
screenBounds.getMinX(),
screenBounds.getMaxX())
* getIntersectionLength(rectY0, rectY1,
screenBounds.getMinY(),
screenBounds.getMaxY());
if (maxIntersection < intersection) {
maxIntersection = intersection;
selectedScreen = screen;
}
}
if (selectedScreen != null) {
return selectedScreen;
}
selectedScreen = Screen.getPrimary();
double minDistance = Double.MAX_VALUE;
for (final Screen screen: screens) {
final Rectangle2D screenBounds = screen.getBounds();
final double dx = getOuterDistance(rectX0, rectX1,
screenBounds.getMinX(),
screenBounds.getMaxX());
final double dy = getOuterDistance(rectY0, rectY1,
screenBounds.getMinY(),
screenBounds.getMaxY());
final double distance = dx * dx + dy * dy;
if (minDistance > distance) {
minDistance = distance;
selectedScreen = screen;
}
}
return selectedScreen;
}
public static Screen getScreenForPoint(final double x, final double y) {
final List<Screen> screens = Screen.getScreens();
// first check whether the point is inside some screen
for (final Screen screen: screens) {
// can't use screen.bounds.contains, because it returns true for
// the min + width point
final Rectangle2D screenBounds = screen.getBounds();
if ((x >= screenBounds.getMinX())
&& (x < screenBounds.getMaxX())
&& (y >= screenBounds.getMinY())
&& (y < screenBounds.getMaxY())) {
return screen;
}
}
// the point is not inside any screen, find the closest screen now
Screen selectedScreen = Screen.getPrimary();
double minDistance = Double.MAX_VALUE;
for (final Screen screen: screens) {
final Rectangle2D screenBounds = screen.getBounds();
final double dx = getOuterDistance(screenBounds.getMinX(),
screenBounds.getMaxX(),
x);
final double dy = getOuterDistance(screenBounds.getMinY(),
screenBounds.getMaxY(),
y);
final double distance = dx * dx + dy * dy;
if (minDistance >= distance) {
minDistance = distance;
selectedScreen = screen;
}
}
return selectedScreen;
}
private static double getIntersectionLength(
final double a0, final double a1,
final double b0, final double b1) {
// (a0 <= a1) && (b0 <= b1)
return (a0 <= b0) ? getIntersectionLengthImpl(b0, b1, a1)
: getIntersectionLengthImpl(a0, a1, b1);
}
private static double getIntersectionLengthImpl(
final double v0, final double v1, final double v) {
// (v0 <= v1)
if (v <= v0) {
return 0;
}
return (v <= v1) ? v - v0 : v1 - v0;
}
private static double getOuterDistance(
final double a0, final double a1,
final double b0, final double b1) {
// (a0 <= a1) && (b0 <= b1)
if (a1 <= b0) {
return b0 - a1;
}
if (b1 <= a0) {
return b1 - a0;
}
return 0;
}
private static double getOuterDistance(final double v0,
final double v1,
final double v) {
// (v0 <= v1)
if (v <= v0) {
return v0 - v;
}
if (v >= v1) {
return v - v1;
}
return 0;
}
/***************************************************************************
* *
* Miscellaneous utilities *
* *
**************************************************************************/
To force initialization of a class
Params: - classToInit –
/**
* To force initialization of a class
* @param classToInit
*/
public static void forceInit(final Class<?> classToInit) {
try {
Class.forName(classToInit.getName(), true,
classToInit.getClassLoader());
} catch (final ClassNotFoundException e) {
throw new AssertionError(e); // Can't happen
}
}
public static boolean assertionEnabled() {
boolean assertsEnabled = false;
assert assertsEnabled = true; // Intentional side-effect !!!
return assertsEnabled;
}
Returns true if the operating system is a form of Windows.
/**
* Returns true if the operating system is a form of Windows.
*/
public static boolean isWindows(){
return PlatformUtil.isWindows();
}
Returns true if the operating system is a form of Mac OS.
/**
* Returns true if the operating system is a form of Mac OS.
*/
public static boolean isMac(){
return PlatformUtil.isMac();
}
Returns true if the operating system is a form of Unix, including Linux.
/**
* Returns true if the operating system is a form of Unix, including Linux.
*/
public static boolean isUnix(){
return PlatformUtil.isUnix();
}
*
Unicode-related utilities *
*
/***************************************************************************
* *
* Unicode-related utilities *
* *
**************************************************************************/
public static String convertUnicode(String src) {
/** The input buffer, index of next character to be read,
* index of one past last character in buffer.
*/
char[] buf;
int bp;
int buflen;
/** The current character.
*/
char ch;
/** The buffer index of the last converted unicode character
*/
int unicodeConversionBp = -1;
buf = src.toCharArray();
buflen = buf.length;
bp = -1;
char[] dst = new char[buflen];
int dstIndex = 0;
while (bp < buflen - 1) {
ch = buf[++bp];
if (ch == '\\') {
if (unicodeConversionBp != bp) {
bp++; ch = buf[bp];
if (ch == 'u') {
do {
bp++; ch = buf[bp];
} while (ch == 'u');
int limit = bp + 3;
if (limit < buflen) {
char c = ch;
int result = Character.digit(c, 16);
if (result >= 0 && c > 0x7f) {
//lexError(pos+1, "illegal.nonascii.digit");
ch = "0123456789abcdef".charAt(result);
}
int d = result;
int code = d;
while (bp < limit && d >= 0) {
bp++; ch = buf[bp];
char c1 = ch;
int result1 = Character.digit(c1, 16);
if (result1 >= 0 && c1 > 0x7f) {
//lexError(pos+1, "illegal.nonascii.digit");
ch = "0123456789abcdef".charAt(result1);
}
d = result1;
code = (code << 4) + d;
}
if (d >= 0) {
ch = (char)code;
unicodeConversionBp = bp;
}
}
//lexError(bp, "illegal.unicode.esc");
} else {
bp--;
ch = '\\';
}
}
}
dst[dstIndex++] = ch;
}
return new String(dst, 0, dstIndex);
}
public static synchronized void loadNativeSwingLibrary() {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
String libName = "prism_common";
if (PrismSettings.verbose) {
System.out.println("Loading Prism common native library ...");
}
NativeLibLoader.loadLibrary(libName);
if (PrismSettings.verbose) {
System.out.println("\tsucceeded.");
}
return null;
});
}
}