com.sun.javafx.Utils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjfx-78-backport Show documentation
Show all versions of openjfx-78-backport Show documentation
This is a backport of OpenJFX 8 to run on Java 7.
The newest version!
/*
* Copyright (c) 2010, 2013, 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;
import java.util.ArrayList;
import java.util.List;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.HPos;
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 com.sun.javafx.stage.StageHelper;
import javafx.geometry.NodeOrientation;
/**
* 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.
*/
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.
*/
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.
*/
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.
*/
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
* above the min value.
*/
public static int clampMin(int value, int min) {
if (value < min) return min;
return value;
}
/**
* Simple utility function which clamps the given value to be strictly
* under the max value.
*/
public static float clampMax(float value, float max) {
if (value > max) return max;
return 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 {@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 *
* *
**************************************************************************/
/**
* Simple helper function which works on both desktop and mobile for
* stripping newlines. The problem we encountered when attempting this in
* FX was that there is no character literal in FX and no way that I could
* see to efficiently create characters representing newline and so forth.
*/
public static String stripNewlines(String s) {
if (s == null) return null;
return s.replace('\n', ' ');
}
/**
* 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.
*/
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 result = new java.util.ArrayList();
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.
*/
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
*/
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.
*
* @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 {@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 {@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 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.
*/
public static Color convertSRGBtoLinearRGB(Color color) {
double[] colors = new double[] { color.getRed(), color.getGreen(), color.getBlue() };
for (int i=0; i List getManaged(Listnodes) {
List managed = new ArrayList();
for (E e : nodes) {
if (e != null && e.isManaged()) {
managed.add(e);
}
}
return managed;
}
/** 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;
}
/**
* Returns a Point2D that represents an x,y location that should safely position
* the given node relative to the given parent node.
*
* If reposition is set to be false, then the node will be positioned with no
* regard to it's position being offscreen. Conversely, setting reposition to be
* true will result in the point being shifted such that the entire node is onscreen.
*
* How this works is largely based on the provided hpos and vpos parameters, with
* the repositioned node trying not to overlap the parent unless absolutely necessary.
*/
public static Point2D pointRelativeTo(Node parent, Node node, HPos hpos, VPos vpos, boolean reposition) {
final double nodeWidth = node.getLayoutBounds().getWidth();
final double nodeHeight = node.getLayoutBounds().getHeight();
return pointRelativeTo(parent, nodeWidth, nodeHeight, hpos, vpos, 0, 0, reposition);
}
public static Point2D pointRelativeTo(Node parent, double anchorWidth, double anchorHeight,
HPos hpos, VPos vpos, boolean reposition)
{
return pointRelativeTo(parent, anchorWidth, anchorHeight, hpos, vpos, 0, 0, reposition);
}
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)
{
double parentXOffset = getOffsetX(parent);
final double parentYOffset = getOffsetY(parent);
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;
}
}
double layoutX = positionX(parentXOffset, parentBounds, anchorWidth, hpos) + dx;
final double layoutY = positionY(parentYOffset, 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 + (dx * 2);
} else {
layoutX = layoutX - parentBounds.getWidth() - anchorWidth;
}
}
if (reposition) {
return pointRelativeTo(parent, anchorWidth, anchorHeight, layoutX, layoutY, hpos, vpos);
} else {
return new Point2D(layoutX, layoutY);
}
}
/**
* Returns a Point2D that represents an x,y location that should safely position
* the given node relative to the given parent node.
*
* The provided x and y values are offsets from the parent node. This allows for
* the node to be positioned relative to the parent using exact coordinates.
*
* If reposition is set to be false, then the node will be positioned with no
* regard to it's position being offscreen. Conversely, setting reposition to be
* true will result in the point being shifted such that the entire node is onscreen.
*/
public static Point2D pointRelativeTo(Node parent, Node node, double x, double y, boolean reposition) {
final Bounds bounds = parent.localToScreen(parent.getBoundsInLocal());
final double layoutX = x + bounds.getMinX();
final double layoutY = y + bounds.getMinY();
if (reposition) {
return pointRelativeTo(parent, node, layoutX, layoutY, null, null);
} else {
return new Point2D(layoutX, layoutY);
}
}
/**
* Returns a Point2D that represents an x,y location that should safely position
* the given node relative to the given parent node.
*
* Note: Unlike other functions provided in this class, the provided x
* and y values are not offsets from the parent node - they are relative
* to the screen. This reduces the utility of this function, and in many cases
* you're better off using the more specific functions provided.
*
* How this works is largely based on the provided hpos and vpos parameters, with
* the repositioned node trying not to overlap the parent unless absolutely necessary.
*
* This function implicitly has the reposition argument set to true, which means
* that the returned Point2D be such that the node will be fully on screen.
*
* Don't use the BASELINE vpos, it doesn't make sense and would produce wrong result.
*/
public static Point2D pointRelativeTo(Node parent, Node node, double screenX,
double screenY, HPos hpos, VPos vpos)
{
final double nodeWidth = node.getLayoutBounds().getWidth();
final double nodeHeight = node.getLayoutBounds().getHeight();
return pointRelativeTo(parent, nodeWidth, nodeHeight, screenX, screenY, hpos, vpos);
}
/**
* 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.
*/
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 double parentOffsetX = getOffsetX(parent);
final double parentOffsetY = getOffsetY(parent);
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(parentOffsetX, 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(parentOffsetX, 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(parentOffsetY, parentBounds, height, getVPosOpposite(hpos,vpos));
}
// don't let the node out of the top of the current screen
if (finalScreenY < screenBounds.getMinY()) {
finalScreenY = positionY(parentOffsetY, 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);
}
/**
* Returns a Point2D that represents an x,y location that should safely position
* a node on screen assuming its width and height values are equal to the arguments given
* to this function.
*
* In this situation, the provided screenX and screenY values are in screen coordinates, so
* the reposition value is implicitly set to true. This means that after calling
* this function you'll have a Point2D object representing new screen coordinates.
*/
public static Point2D pointRelativeTo(Window parent, double width, double height, double screenX, double screenY) {
return pointRelativeTo(parent, width, height, screenX, screenY, null, null);
}
/**
* To facilitate multiple types of parent object, we unfortunately must allow for
* Objects to be passed in. This method handles determining the x-axis offset of the
* given Object from the screens (0,0) position. If the Object type is not supported,
* 0 will be returned.
*/
private static double getOffsetX(Object obj) {
if (obj instanceof Node) {
Scene scene = ((Node)obj).getScene();
if ((scene == null) || (scene.getWindow() == null)) {
return 0;
}
return scene.getX() + scene.getWindow().getX();
} else if (obj instanceof Window) {
return ((Window)obj).getX();
} 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 y-axis offset of the
* given Object from the screens (0,0) position. If the Object type is not supported,
* 0 will be returned.
*/
private static double getOffsetY(Object obj) {
if (obj instanceof Node) {
Scene scene = ((Node)obj).getScene();
if ((scene == null) || (scene.getWindow() == null)) {
return 0;
}
return scene.getY() + scene.getWindow().getY();
} else if (obj instanceof Window) {
return ((Window)obj).getY();
} else {
return 0;
}
}
/**
* Utility function that returns the x-axis position that an object should be positioned at,
* given the parent x-axis offset, the parents bounds, the width of the object, and
* the required HPos.
*/
private static double positionX(double parentXOffset, 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 parentXOffset + parentBounds.getMinX();
} else if (hpos == HPos.RIGHT) {
return parentXOffset + parentBounds.getMaxX();
} else if (hpos == HPos.LEFT) {
return parentXOffset + parentBounds.getMinX() - width;
} else {
return 0;
}
}
/**
* Utility function that returns the y-axis position that an object should be positioned at,
* given the parent y-axis offset, the parents 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(double parentYOffset, Bounds parentBounds, double height, VPos vpos) {
if (vpos == VPos.BOTTOM) {
return parentYOffset + parentBounds.getMaxY();
} else if (vpos == VPos.CENTER) {
return parentYOffset + parentBounds.getMinY();
} else if (vpos == VPos.TOP) {
return parentYOffset + 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.
*/
private static Bounds getBounds(Object obj) {
if (obj instanceof Node) {
return ((Node)obj).localToScene(((Node)obj).getBoundsInLocal());
} else if (obj instanceof Window) {
final Window window = (Window)obj;
return new BoundingBox(0, 0, 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 allStages = StageHelper.getStages();
for (final Stage stage: allStages) {
if (stage.isFullScreen() && (getScreen(stage) == screen)) {
return true;
}
}
return false;
}
/*
* Returns true if the primary Screen has VGA dimensions, in landscape or portrait mode.
*/
public static boolean isVGAScreen() {
Rectangle2D bounds = Screen.getPrimary().getBounds();
return ((bounds.getWidth() == 640 && bounds.getHeight() == 480) ||
(bounds.getWidth() == 480 && bounds.getHeight() == 640));
}
/*
* 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.
*/
public static Screen getScreen(Object obj) {
// handle dual monitors (be careful of minX/minY vs width/height).
// we create a rectangle representing the menubar menu item...
final double offsetX = getOffsetX(obj);
final double offsetY = getOffsetY(obj);
final Bounds parentBounds = getBounds(obj);
final Rectangle2D rect = new Rectangle2D(
offsetX + parentBounds.getMinX(),
offsetY + parentBounds.getMinY(),
parentBounds.getWidth(),
parentBounds.getHeight());
return getScreenForRectangle(rect);
}
public static Screen getScreenForRectangle(final Rectangle2D rect) {
final List 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 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 *
* *
**************************************************************************/
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.
*/
public static boolean isWindows(){
return PlatformUtil.isWindows();
}
/**
* 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.
*/
public static boolean isUnix(){
return PlatformUtil.isUnix();
}
/***************************************************************************
* *
* 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);
}
}