com.codename1.ui.plaf.Border Maven / Gradle / Ivy
/*
* Copyright (c) 2008, 2010, 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.codename1.ui.plaf;
import com.codename1.ui.Component;
import com.codename1.ui.Display;
import com.codename1.ui.Font;
import com.codename1.ui.Graphics;
import com.codename1.ui.Image;
import com.codename1.ui.ImageFactory;
import com.codename1.ui.Painter;
import com.codename1.ui.RGBImage;
import com.codename1.ui.geom.Rectangle;
/**
* Base class that allows us to render a border for a component, a border is drawn before
* the component and is drawn within the padding region of the component. It is the
* responsibility of the component not to draw outside of the border line.
* This class can be extended to provide additional border types and custom made
* border types.
*
A border can optionally paint the background of the component, this depends on
* the border type and is generally required for rounded borders that "know" the area
* that should be filled.
*
* @author Shai Almog
*/
public class Border {
private static Border defaultBorder = Border.createEtchedRaised(0x020202, 0xBBBBBB);
private static final int TYPE_EMPTY = 0;
private static final int TYPE_LINE = 1;
private static final int TYPE_ROUNDED = 2;
private static final int TYPE_ROUNDED_PRESSED = 3;
private static final int TYPE_ETCHED_LOWERED = 4;
private static final int TYPE_ETCHED_RAISED = 5;
private static final int TYPE_BEVEL_RAISED = 6;
private static final int TYPE_BEVEL_LOWERED = 7;
private static final int TYPE_IMAGE = 8;
private static final int TYPE_COMPOUND = 9;
private static final int TYPE_IMAGE_HORIZONTAL = 10;
private static final int TYPE_IMAGE_VERTICAL = 11;
private static final int TYPE_DASHED = 12;
private static final int TYPE_DOTTED = 13;
private static final int TYPE_DOUBLE = 14;
private static final int TYPE_GROOVE = 15;
private static final int TYPE_RIDGE = 16;
private static final int TYPE_INSET = 17;
private static final int TYPE_OUTSET = 18;
private static final int TYPE_IMAGE_SCALED = 19;
private static final int TYPE_UNDERLINE = 21;
// variables are package protected for the benefit of the resource editor!
int type;
Image[] images;
private Image[] specialTile;
private Rectangle trackComponent;
/**
* Indicates whether theme colors should be used or whether colors are specified
* in the border
*/
boolean themeColors;
int colorA;
int colorB;
int colorC;
int colorD;
float thickness = 0;
boolean millimeters;
int arcWidth;
int arcHeight;
boolean outline = true;
Border pressedBorder;
Border focusBorder;
Border [] compoundBorders;
Border outerBorder; // A border added outside of this border (Used for CSS outline property, but can also be used for other purposes)
String borderTitle; // border title, currently supported only for line borders
private boolean paintOuterBorderFirst;
private static final int TITLE_MARGIN = 10;
private static final int TITLE_SPACE = 5;
private static Border empty;
private boolean emptyType;
/**
* Prevents usage of new operator, use the factory methods in the class or subclass
* to create new border types.
*/
protected Border() {
}
/**
* If a border is a horizontal image border it can be mirrored which is useful for an RTL scenario
*
* @return the current border or a mirrored instance
*/
public Border mirrorBorder() {
if(type == TYPE_IMAGE_HORIZONTAL) {
return createHorizonalImageBorder(images[1].mirror(), images[0].mirror(), images[2].mirror());
}
return this;
}
/**
* This method is designed mainly for the purpose of creating an arrow that will track a specific component using the image border
* the tile given would be an arrow like image just like the ones used for the top/bottom/left/right images. This image would be positioned
* so it points at the center of the track component
*
* @param tileTop an image that will replace one of the tiles on the top if the track component is there
* @param tileBottom an image that will replace one of the tiles on the bottom if the track component is there
* @param tileLeft an image that will replace one of the tiles on the left if the track component is there
* @param tileRight an image that will replace one of the tiles on the right if the track component is there
* @param trackComponent the component that will be tracked for the positioning of the tile
*/
public void setImageBorderSpecialTile(Image tileTop, Image tileBottom, Image tileLeft, Image tileRight, Component trackComponent) {
specialTile = new Image[] {tileTop, tileBottom, tileLeft, tileRight};
this.trackComponent = new Rectangle(trackComponent.getAbsoluteX(), trackComponent.getAbsoluteY(), trackComponent.getWidth(), trackComponent.getHeight());
}
/**
* The track component is an area to which an arrow based border should point at.
* It's assumed to be outside of the border region in absolute screen coordinates.
*
* @return the rectangle to point at or null if this isn't an arrow border
*/
protected Rectangle getTrackComponent() {
return trackComponent;
}
/**
* In the case of an arrow border the track component lets us track the position
* to which the border is pointing
* @param trackComponent the track component
*/
public void setTrackComponent(Component trackComponent) {
this.trackComponent = new Rectangle(trackComponent.getAbsoluteX(), trackComponent.getAbsoluteY(), trackComponent.getWidth(), trackComponent.getHeight());
}
/**
* In the case of an arrow border the track component lets us track the position
* to which the border is pointing
* @param trackComponent the track component
*/
public void setTrackComponent(Rectangle trackComponent) {
this.trackComponent = trackComponent;
}
/**
* This method is designed mainly for the purpose of creating an arrow that will track a specific component using the image border
* the tile given would be an arrow like image just like the ones used for the top/bottom/left/right images. This image would be positioned
* so it points at the center of the track component
*
* @param tileTop an image that will replace one of the tiles on the top if the track component is there
* @param tileBottom an image that will replace one of the tiles on the bottom if the track component is there
* @param tileLeft an image that will replace one of the tiles on the left if the track component is there
* @param tileRight an image that will replace one of the tiles on the right if the track component is there
* @param trackComponent the component that will be tracked for the positioning of the tile
*/
public void setImageBorderSpecialTile(Image tileTop, Image tileBottom, Image tileLeft, Image tileRight, Rectangle trackComponent) {
specialTile = new Image[] {tileTop, tileBottom, tileLeft, tileRight};
this.trackComponent = trackComponent;
}
/**
* Cleans the tile tracking state allowing the garbage collector to pick up the component and the image data
*/
public void clearImageBorderSpecialTile() {
specialTile = null;
trackComponent = null;
}
/**
* Ads a border that wraps this border
*
* @param outer The outer border
*/
public void addOuterBorder(Border outer) {
outerBorder=outer;
}
/**
* Returns the minimum size required to properly display this border, normally this
* is 0 but a border might deem itself undisplayable with too small a size e.g. for
* the case of an image border the minimum height would be top + bottom and the minimum
* width would be left+right
*
* @return 0 if not applicable or a dimension if it is.
*/
public int getMinimumHeight() {
if(images != null) {
if(images.length < 4) {
if(type == TYPE_IMAGE_HORIZONTAL) {
return images[0].getHeight();
} else {
return images[0].getHeight() + images[1].getHeight();
}
}
Image topLeft = images[4];
Image bottomRight = images[7];
return topLeft.getHeight() + bottomRight.getHeight();
}
return 0;
}
/**
* Returns the minimum size required to properly display this border, normally this
* is 0 but a border might deem itself undisplayable with too small a size e.g. for
* the case of an image border the minimum height would be top + bottom and the minimum
* width would be left+right
*
* @return 0 if not applicable or a dimension if it is.
*/
public int getMinimumWidth() {
if(images != null) {
if(images.length < 4) {
if(type == TYPE_IMAGE_HORIZONTAL) {
return images[0].getWidth() + images[1].getWidth();
} else {
return images[0].getWidth();
}
}
Image topLeft = images[4];
Image topRight = images[5];
return topLeft.getWidth() + topRight.getWidth();
}
return 0;
}
/**
* Returns an empty border, this is mostly useful for overriding components that
* have a border by default
*
* @return a border than draws nothing
* @deprecated use createEmpty instead
*/
public static Border getEmpty() {
if(empty == null) {
empty = new Border();
}
return empty;
}
/**
* Creates an empty border, this is useful where we don't want a border for a
* component but want a focus border etc...
*
* @return a border than draws nothing
*/
public static Border createEmpty() {
Border b = new Border();
b.emptyType = true;
return b;
}
/**
* Indicates whether this is an empty border
* @return true if this is an empty border
*/
public boolean isEmptyBorder() {
return emptyType;
}
/**
* The given top/bottom/left/right images are tiled appropriately across the matching sides of the border and the corners are placed
* as expected in the four corners. The background image is optional and it will be tiled in the background if necessary.
*
*
By default this border does not override background unless a background image is specified
*
* @param top the image of the top line
* @param bottom the image of the bottom line
* @param left the image of the left line
* @param right the image of the right line
* @param topLeft the image of the top left corner
* @param topRight the image of the top right corner
* @param bottomLeft the image of the bottom left corner
* @param bottomRight the image of the bottom right corner
* @param background the image of the background (optional)
* @return new border instance
*/
public static Border createImageBorder(Image top, Image bottom, Image left, Image right, Image topLeft, Image topRight,
Image bottomLeft, Image bottomRight, Image background) {
Border b = new Border();
b.type = TYPE_IMAGE;
b.images = new Image[] {top, bottom, left, right, topLeft, topRight, bottomLeft,
bottomRight, background};
return b;
}
/**
* The given image is spliced into 9 pieces based on the provided top, right, bottom, and left insets, and the resulting
* sub-images are used to form a 9-piece image border via {@link #createImageBorder(com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image, com.codename1.ui.Image) }
*
*
Insets are all given in a (u,v) coordinate space where (0,0) is the top-left corner of the image, and (1.0, 1.0) is the bottom-right corner of the image.
* @param img The image to be used as a background image and spliced.
* @param topInset
* @param rightInset
* @param bottomInset
* @param leftInset
* @return A 9-piece image border.
*/
public static Border createImageSplicedBorder(Image img, double topInset, double rightInset, double bottomInset, double leftInset) {
Border b = new Border();
b.type = TYPE_IMAGE;
int w = img.getWidth();
int h = img.getHeight();
int topInsetPx = (int)Math.round(h * topInset);
int leftInsetPx = (int)Math.round(w * leftInset);
int rightInsetPx = (int)Math.round(w * rightInset);
int bottomInsetPx = (int)Math.round(h * bottomInset);
//We need to make sure that every splice has a positive width and height
if (topInsetPx + bottomInsetPx >= h) {
bottomInsetPx = h - topInsetPx -1;
if (bottomInsetPx < 1) {
bottomInsetPx = 1;
topInsetPx = h - bottomInsetPx - 1;
}
}
if (leftInsetPx + rightInsetPx >= w) {
rightInsetPx = w - leftInsetPx - 1;
if (rightInsetPx < 1) {
rightInsetPx = 1;
leftInsetPx = w - rightInsetPx - 1;
}
}
Image top = img.subImage(leftInsetPx, 0, w - leftInsetPx - rightInsetPx, topInsetPx, true);
Image bottom = img.subImage(leftInsetPx, h - bottomInsetPx, w - leftInsetPx - rightInsetPx, bottomInsetPx, true);
Image left = img.subImage(0, topInsetPx, leftInsetPx, h - topInsetPx - bottomInsetPx, true);
Image right = img.subImage(w - rightInsetPx, topInsetPx, rightInsetPx, h - topInsetPx - bottomInsetPx, true);
Image topLeft = img.subImage(0, 0, leftInsetPx, topInsetPx, true);
Image topRight = img.subImage(w - rightInsetPx, 0, rightInsetPx, topInsetPx, true);
Image bottomLeft = img.subImage(0, h - bottomInsetPx, leftInsetPx, bottomInsetPx, true);
Image bottomRight = img.subImage(w - rightInsetPx, h - bottomInsetPx, rightInsetPx, bottomInsetPx, true);
Image background = img.subImage(leftInsetPx, topInsetPx, w - leftInsetPx - rightInsetPx, h - topInsetPx - bottomInsetPx, true);
b.images = new Image[] {top, bottom, left, right, topLeft, topRight, bottomLeft,
bottomRight, background};
return b;
}
/**
* The given top/bottom/left/right images are scaled appropriately across the matching sides of the border and the corners are placed
* as expected in the four corners. The background image is optional and it will be tiled in the background if necessary.
*
* By default this border does not override background unless a background image is specified
*
* @param top the image of the top line
* @param bottom the image of the bottom line
* @param left the image of the left line
* @param right the image of the right line
* @param topLeft the image of the top left corner
* @param topRight the image of the top right corner
* @param bottomLeft the image of the bottom left corner
* @param bottomRight the image of the bottom right corner
* @param background the image of the background (optional)
* @return new border instance
*/
public static Border createImageScaledBorder(Image top, Image bottom, Image left, Image right, Image topLeft, Image topRight,
Image bottomLeft, Image bottomRight, Image background) {
Border b = new Border();
b.type = TYPE_IMAGE_SCALED;
b.images = new Image[] {top, bottom, left, right, topLeft, topRight, bottomLeft,
bottomRight, background};
return b;
}
/**
* This is an image border that can only grow horizontally
*
* @param left the image of the left side
* @param right the image of the right side
* @param center the image of the center
* @return new border instance
*/
public static Border createHorizonalImageBorder(Image left, Image right, Image center) {
Border b = new Border();
b.type = TYPE_IMAGE_HORIZONTAL;
b.images = new Image[] {left, right, center};
return b;
}
/**
* This is an image border that can only grow vertically
*
* @param top the image of the top
* @param bottom the image of the bottom
* @param center the image of the center
* @return new border instance
*/
public static Border createVerticalImageBorder(Image top, Image bottom, Image center) {
Border b = new Border();
b.type = TYPE_IMAGE_VERTICAL;
b.images = new Image[] {top, bottom, center};
return b;
}
/**
* The given images are tiled appropriately across the matching side of the border, rotated and placed
* as expected in the four corners. The background image is optional and it will be tiled in
* the background if necessary.
*
By default this border does not override background unless a background image is specified.
*
Notice that this version of the method is potentially much more efficient since images
* are rotated internally and this might save quite a bit of memory!
*
The top and topLeft images must be square! The width and height of these images
* must be equal otherwise rotation won't work as you expect.
*
* @param top the image of the top line
* @param topLeft the image of the top left corner
* @param background the image of the background (optional)
* @return new border instance
*/
public static Border createImageBorder(Image top, Image topLeft, Image background) {
Border b = new Border();
b.type = TYPE_IMAGE;
b.images = new Image[] {top, top.rotate(180), top.rotate(270), top.rotate(90), topLeft, topLeft.rotate(90),
topLeft.rotate(270), topLeft.rotate(180), background};
return b;
}
/**
* Creates a line border that uses the color of the component foreground for drawing
*
* @param thickness thickness of the border in pixels
* @return new border instance
*/
public static Border createLineBorder(int thickness) {
Border b = new Border();
b.type = TYPE_LINE;
b.themeColors = true;
b.thickness = thickness;
b.millimeters = false;
return b;
}
/**
* Creates a line border that uses the color of the component foreground for drawing
*
* @param thickness thickness of the border in millimeters
* @return new border instance
*/
public static Border createLineBorder(float thickness) {
Border b = new Border();
b.type = TYPE_LINE;
b.themeColors = true;
b.thickness = thickness;
b.millimeters = true;
return b;
}
/**
* Creates an underline border that uses the color of the component foreground for drawing
*
* @deprecated due to a spelling mistake. Use {@link #createUnderlineBorder(int thickness)}
* @param thickness thickness of the border in pixels
* @return new border instance
*/
public static Border createUndelineBorder(int thickness) {
Border b = new Border();
b.type = TYPE_UNDERLINE;
b.themeColors = true;
b.thickness = thickness;
return b;
}
/**
* Creates an underline border that uses the color of the component foreground for drawing
*
* @param thickness thickness of the border in pixels
* @return new border instance
*/
public static Border createUnderlineBorder(int thickness) {
Border b = new Border();
b.type = TYPE_UNDERLINE;
b.themeColors = true;
b.thickness = thickness;
return b;
}
/**
* Creates an underline border that uses the color of the component foreground for drawing
*
* @deprecated due to a spelling mistake. Use {@link #createUnderlineBorder(float thickness)}
* @param thickness thickness of the border in millimeters
* @return new border instance
*/
public static Border createUndelineBorder(float thickness) {
Border b = new Border();
b.type = TYPE_UNDERLINE;
b.themeColors = true;
b.thickness = thickness;
b.millimeters = true;
return b;
}
/**
* Creates an underline border that uses the color of the component foreground for drawing
*
* @param thickness thickness of the border in millimeters
* @return new border instance
*/
public static Border createUnderlineBorder(float thickness) {
Border b = new Border();
b.type = TYPE_UNDERLINE;
b.themeColors = true;
b.thickness = thickness;
b.millimeters = true;
return b;
}
/**
* Creates an underline border that uses the given color
*
* @param thickness thickness of the border in pixels
* @param color the color
* @return new border instance
*/
public static Border createUnderlineBorder(int thickness, int color) {
Border b = new Border();
b.type = TYPE_UNDERLINE;
b.themeColors = false;
b.thickness = thickness;
b.colorA = color;
return b;
}
/**
* Creates an underline border that uses the given color
*
* @param thickness thickness of the border in millimeters
* @param color the color
* @return new border instance
*/
public static Border createUnderlineBorder(float thickness, int color) {
Border b = new Border();
b.type = TYPE_UNDERLINE;
b.themeColors = false;
b.thickness = thickness;
b.millimeters = true;
b.colorA = color;
return b;
}
/**
* Creates a dotted border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createDottedBorder(int thickness,int color) {
return createCSSBorder(TYPE_DOTTED, thickness, color);
}
/**
* Creates a dashed border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createDashedBorder(int thickness,int color) {
return createCSSBorder(TYPE_DASHED, thickness, color);
}
/**
* Creates a double border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createDoubleBorder(int thickness,int color) {
return createCSSBorder(TYPE_DOUBLE, thickness, color);
}
/**
* Creates a dotted border with the specified thickness and the theme colors
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createDottedBorder(int thickness) {
return createCSSBorder(TYPE_DOTTED, thickness);
}
/**
* Creates a dashed border with the specified thickness and the theme colors
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createDashedBorder(int thickness) {
return createCSSBorder(TYPE_DASHED, thickness);
}
/**
* Creates a double border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createDoubleBorder(int thickness) {
return createCSSBorder(TYPE_DOUBLE, thickness);
}
/**
* Creates an outset border with the specified thickness and theme colors
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createOutsetBorder(int thickness) {
return createCSSBorder(TYPE_OUTSET, thickness);
}
/**
* Creates an outset border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createOutsetBorder(int thickness,int color) {
return createCSSBorder(TYPE_OUTSET, thickness,color);
}
/**
* Creates an inset border with the specified thickness and theme colors
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createInsetBorder(int thickness) {
return createCSSBorder(TYPE_INSET, thickness);
}
/**
* Creates an inset border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createInsetBorder(int thickness,int color) {
return createCSSBorder(TYPE_INSET, thickness,color);
}
/**
* Creates a groove border with the specified thickness and theme colors
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createGrooveBorder(int thickness) {
return createCSSBorder(TYPE_GROOVE, thickness);
}
/**
* Creates a groove border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createGrooveBorder(int thickness,int color) {
return createCSSBorder(TYPE_GROOVE, thickness,color);
}
/**
* Creates a ridge border with the specified thickness and theme colors
*
* @param thickness The border thickness in pixels
* @return The border
*/
public static Border createRidgeBorder(int thickness) {
return createCSSBorder(TYPE_RIDGE, thickness);
}
/**
* Creates a ridge border with the specified thickness and color
*
* @param thickness The border thickness in pixels
* @param color The border color
* @return The border
*/
public static Border createRidgeBorder(int thickness,int color) {
return createCSSBorder(TYPE_RIDGE, thickness,color);
}
private static Border createCSSBorder(int type,int thickness) {
Border b = new Border();
b.type = type;
b.themeColors = true;
b.thickness = thickness;
return b;
}
private static Border createCSSBorder(int type,int thickness,int color) {
Border b = new Border();
b.type = type;
b.colorA = color;
b.thickness = thickness;
return b;
}
/**
* Creates a line border with the specified title
*
* @param thickness thickness of the border in pixels
* @param title The borders title
* @return new border instance
*/
public static Border createLineBorder(int thickness, String title) {
Border b = new Border();
b.type = TYPE_LINE;
b.themeColors = true;
b.thickness = thickness;
b.borderTitle=title;
return b;
}
/**
* Creates a line border that uses the given color for the component
*
* @param thickness thickness of the border in pixels
* @param color the color for the border
* @param title The borders title
* @return new border instance
*/
public static Border createLineBorder(int thickness, int color, String title) {
Border b = new Border();
b.type = TYPE_LINE;
b.themeColors = false;
b.thickness = thickness;
b.colorA = color;
b.borderTitle=title;
return b;
}
/**
* Creates a line border that uses the given color for the component
*
* @param thickness thickness of the border in pixels
* @param color the color for the border
* @return new border instance
*/
public static Border createLineBorder(int thickness, int color) {
Border b = new Border();
b.type = TYPE_LINE;
b.themeColors = false;
b.thickness = thickness;
b.colorA = color;
return b;
}
/**
* Creates a line border that uses the given color for the component
*
* @param thickness thickness of the border in millimeters
* @param color the color for the border
* @return new border instance
*/
public static Border createLineBorder(float thickness, int color) {
Border b = new Border();
b.type = TYPE_LINE;
b.themeColors = false;
b.thickness = thickness;
b.millimeters = true;
b.colorA = color;
return b;
}
/**
* Creates a rounded corner border that uses the color of the component foreground for drawing.
* Due to technical issues (lack of shaped clipping) performance and memory overhead of round
* borders can be low if used with either a bgImage or translucency!
*
This border overrides any painter used on the component and would ignor such a painter.
*
* @param arcWidth the horizontal diameter of the arc at the four corners.
* @param arcHeight the vertical diameter of the arc at the four corners.
* @return new border instance
* @deprecated the performance of round rect borders is REALLY slow, we recommend people use image borders
* which are faster, more portable and better looking
*/
public static Border createRoundBorder(int arcWidth, int arcHeight) {
Border b = new Border();
b.type = TYPE_ROUNDED;
b.themeColors = true;
b.arcHeight = arcHeight;
b.arcWidth = arcWidth;
return b;
}
/**
* Creates a rounded corner border that uses the color of the component foreground for drawing.
* Due to technical issues (lack of shaped clipping) performance and memory overhead of round
* borders can be low if used with either a bgImage or translucency!
*
This border overrides any painter used on the component and would ignor such a painter.
*
* @param arcWidth the horizontal diameter of the arc at the four corners.
* @param arcHeight the vertical diameter of the arc at the four corners.
* @param outline whether the round rect border outline should be drawn
* @return new border instance
* @deprecated the performance of round rect borders is REALLY slow, we recommend people use image borders
* which are faster, more portable and better looking
*/
public static Border createRoundBorder(int arcWidth, int arcHeight, boolean outline) {
Border b = createRoundBorder(arcWidth, arcHeight);
b.outline = outline;
return b;
}
/**
* Creates a rounded border that uses the given color for the component.
* Due to technical issues (lack of shaped clipping) performance and memory overhead of round
* borders can be low if used with either a bgImage or translucency!
*
This border overrides any painter used on the component and would ignor such a painter.
*
* @param arcWidth the horizontal diameter of the arc at the four corners.
* @param arcHeight the vertical diameter of the arc at the four corners.
* @param color the color for the border
* @return new border instance
* @deprecated the performance of round rect borders is REALLY slow, we recommend people use image borders
* which are faster, more portable and better looking
*/
public static Border createRoundBorder(int arcWidth, int arcHeight, int color) {
Border b = new Border();
b.type = TYPE_ROUNDED;
b.themeColors = false;
b.colorA = color;
b.arcHeight = arcHeight;
b.arcWidth = arcWidth;
return b;
}
/**
* Creates a rounded border that uses the given color for the component.
* Due to technical issues (lack of shaped clipping) performance and memory overhead of round
* borders can be low if used with either a bgImage or translucency!
*
This border overrides any painter used on the component and would ignor such a painter.
*
* @param arcWidth the horizontal diameter of the arc at the four corners.
* @param arcHeight the vertical diameter of the arc at the four corners.
* @param color the color for the border
* @param outline whether the round rect border outline should be drawn
* @return new border instance
* @deprecated the performance of round rect borders is REALLY slow, we recommend people use image borders
* which are faster, more portable and better looking
*/
public static Border createRoundBorder(int arcWidth, int arcHeight, int color, boolean outline) {
Border b = createRoundBorder(arcWidth, arcHeight, color);
b.outline = outline;
return b;
}
/**
* Creates a lowered etched border with default colors, highlight is derived
* from the component and shadow is a plain dark color
*
* @return new border instance
*/
public static Border createEtchedLowered() {
Border b = new Border();
b.type = TYPE_ETCHED_LOWERED;
b.themeColors = true;
return b;
}
/**
* Creates a raised etched border with the given colors
*
* @param highlight color RGB value
* @param shadow color RGB value
* @return new border instance
*/
public static Border createEtchedLowered(int highlight, int shadow) {
Border b = new Border();
b.type = TYPE_ETCHED_LOWERED;
b.themeColors = false;
b.colorA = shadow;
b.colorB = highlight;
return b;
}
/**
* Creates a lowered etched border with default colors, highlight is derived
* from the component and shadow is a plain dark color
*
* @return new border instance
*/
public static Border createEtchedRaised() {
Border b = new Border();
b.type = TYPE_ETCHED_RAISED;
b.themeColors = true;
b.thickness = 2;
return b;
}
/**
* Creates a raised etched border with the given colors
*
* @param highlight color RGB value
* @param shadow color RGB value
* @return new border instance
*/
public static Border createEtchedRaised(int highlight, int shadow) {
Border b = new Border();
b.type = TYPE_ETCHED_RAISED;
b.themeColors = false;
b.colorA = highlight;
b.colorB = shadow;
b.thickness = 2;
return b;
}
/**
* {{@inheritDoc}}
*/
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == getClass()) {
Border b = (Border)obj;
if ((b.type==TYPE_COMPOUND) && (type==TYPE_COMPOUND)) {
for(int i=Component.TOP;i<=Component.RIGHT;i++) {
if (!isSame(compoundBorders[i], b.compoundBorders[i])) {
return false;
}
}
return true;
}
boolean v = ((themeColors==b.themeColors) &&
(type==b.type) &&
(thickness==b.thickness) &&
(colorA==b.colorA) &&
(colorB==b.colorB) &&
(colorC==b.colorC) &&
(colorD==b.colorD) &&
(arcWidth==b.arcWidth) &&
(arcHeight==b.arcHeight) &&
(outline==b.outline) &&
(isSame(borderTitle, b.borderTitle)) &&
(isSame(outerBorder, b.outerBorder))
);
if(v && (type == TYPE_IMAGE || type == TYPE_IMAGE_HORIZONTAL || type == TYPE_IMAGE_VERTICAL || type == TYPE_IMAGE_SCALED)) {
int ilen = images.length;
for(int iter = 0 ; iter < ilen ; iter++) {
if(images[iter] != b.images[iter]) {
return false;
}
}
}
return v;
}
return false;
}
/**
* Compares two object including the scenario one of them is null (thus avoiding equals pitfalls)
*
* @param obj1 The first object to compare
* @param obj2 The second object to compare
* @return true if the two object are equal (or both null), false otherwise
*/
private static boolean isSame(Object obj1,Object obj2) {
if (obj1==null) {
return (obj2==null);
} else if (obj2==null) {
return (obj1==null);
}
return obj1.equals(obj2);
}
/**
* Creates a border that is comprised of multiple border types so one border type can be used on top
* while another one can be used at the bottom. Notice that this doesn't work well with all border types (e.g. image borders)
* @param top the top border
* @param bottom the bottom border
* @param left the left border
* @param right the right border
* @return a compound border
*/
public static Border createCompoundBorder(Border top, Border bottom, Border left, Border right) {
if ((top != null && !top.isRectangleType()) ||
(bottom != null && !bottom.isRectangleType()) ||
(left != null && !left.isRectangleType()) ||
(right != null && !right.isRectangleType())) {
throw new IllegalArgumentException("Compound Border can be created "
+ "only from Rectangle types Borders");
}
if ((isSame(top, bottom)) && (isSame(top, left)) && (isSame(top, right))) {
return top; // Borders are all the same, returning one of them instead of creating a compound border which is more resource consuming
}
Border b = new Border();
b.type = TYPE_COMPOUND;
b.compoundBorders = new Border[4];
b.compoundBorders[Component.TOP] = top;
b.compoundBorders[Component.BOTTOM] = bottom;
b.compoundBorders[Component.LEFT] = left;
b.compoundBorders[Component.RIGHT] = right;
// Calculates the thickness of the entire border as the maximum of all 4 sides
b.thickness=0;
for(int i=Component.TOP;i<=Component.RIGHT;i++) {
if (b.compoundBorders[i]!=null) {
int sideThickness = (int)b.compoundBorders[i].thickness;
if (sideThickness>b.thickness) {
b.thickness=sideThickness;
}
}
}
return b;
}
/**
* Returns true if installing this border will override the painting of the component background
*
* @return true if this border replaces the painter
*/
public boolean isBackgroundPainter() {
return type == TYPE_ROUNDED || type == TYPE_ROUNDED_PRESSED || type == TYPE_IMAGE
|| type == TYPE_IMAGE_HORIZONTAL || type == TYPE_IMAGE_VERTICAL || type == TYPE_IMAGE_SCALED;
}
/**
* Returns true if this border type is a rectangle border.
*
* @return true if this border is rectangle
*/
public boolean isRectangleType() {
return type == TYPE_LINE || type == TYPE_BEVEL_LOWERED ||
type == TYPE_BEVEL_RAISED || type == TYPE_ETCHED_LOWERED ||
type == TYPE_ETCHED_RAISED || type == TYPE_COMPOUND
|| type == TYPE_EMPTY || type==TYPE_DOTTED || type==TYPE_DASHED || type==TYPE_DOUBLE
|| type == TYPE_OUTSET || type == TYPE_INSET || type == TYPE_GROOVE || type == TYPE_RIDGE;
}
/**
* Creates a lowered bevel border with default colors, highlight is derived
* from the component and shadow is a plain dark color
*
* @return new border instance
*/
public static Border createBevelLowered() {
Border b = new Border();
b.type = TYPE_BEVEL_LOWERED;
b.themeColors = true;
b.thickness = 2;
return b;
}
/**
* Creates a raised bevel border with the given colors
*
* @param highlightOuter RGB of the outer edge of the highlight area
* @param highlightInner RGB of the inner edge of the highlight area
* @param shadowOuter RGB of the outer edge of the shadow area
* @param shadowInner RGB of the inner edge of the shadow area
* @return new border instance
*/
public static Border createBevelLowered(int highlightOuter, int highlightInner,
int shadowOuter, int shadowInner) {
Border b = new Border();
b.type = TYPE_BEVEL_LOWERED;
b.themeColors = false;
b.colorA = highlightOuter;
b.colorB = highlightInner;
b.colorC = shadowOuter;
b.colorD = shadowInner;
b.thickness = 2;
return b;
}
/**
* Creates a lowered bevel border with default colors, highlight is derived
* from the component and shadow is a plain dark color
*
* @return new border instance
*/
public static Border createBevelRaised() {
Border b = new Border();
b.type = TYPE_BEVEL_RAISED;
b.themeColors = true;
b.thickness = 2;
return b;
}
/**
* Creates a raised bevel border with the given colors
*
* @param highlightOuter RGB of the outer edge of the highlight area
* @param highlightInner RGB of the inner edge of the highlight area
* @param shadowOuter RGB of the outer edge of the shadow area
* @param shadowInner RGB of the inner edge of the shadow area
* @return new border instance
*/
public static Border createBevelRaised(int highlightOuter, int highlightInner,
int shadowOuter, int shadowInner) {
Border b = new Border();
b.type = TYPE_BEVEL_RAISED;
b.themeColors = false;
b.colorA = highlightOuter;
b.colorB = highlightInner;
b.colorC = shadowOuter;
b.colorD = shadowInner;
b.thickness = 2;
return b;
}
/**
* Allows us to define a border that will act as the pressed version of this border
*
* @param pressed a border that will be returned by the pressed version method
*/
public void setPressedInstance(Border pressed) {
pressedBorder = pressed;
}
/**
* Allows us to define a border that will act as the focused version of this border
*
* @param focused a border that will be returned by the focused version method
* @deprecated use the getSelectedStyle() method in the component class
*/
public void setFocusedInstance(Border focused) {
focusBorder = focused;
}
/**
* Returns the focused version of the border if one is installed
*
* @return a focused version of this border if one exists
* @deprecated use the getSelectedStyle() method in the component class
*/
public Border getFocusedInstance() {
if(focusBorder != null) {
return focusBorder;
}
return this;
}
/**
* Returns the pressed version of the border if one is set by the user
*
* @return the pressed instance of this border
*/
public Border getPressedInstance() {
if(pressedBorder != null) {
return pressedBorder;
}
return this;
}
/**
* When applied to buttons borders produce a version that reverses the effects
* of the border providing a pressed feel
*
* @return a border that will make the button feel pressed
*/
public Border createPressedVersion() {
if(pressedBorder != null) {
return pressedBorder;
}
switch(type) {
case TYPE_LINE:
if(millimeters) {
return createLineBorder(thickness + 0.1f, colorA);
}
return createLineBorder((int)thickness + 1, colorA);
case TYPE_ETCHED_LOWERED: {
Border b = createEtchedRaised(colorA, colorB);
b.themeColors = themeColors;
return b;
}
case TYPE_ETCHED_RAISED: {
Border b = createEtchedLowered(colorA, colorB);
b.themeColors = themeColors;
return b;
}
case TYPE_BEVEL_RAISED: {
Border b = createBevelLowered(colorA, colorB, colorC, colorD);
b.themeColors = themeColors;
return b;
}
case TYPE_BEVEL_LOWERED: {
Border b = createBevelRaised(colorA, colorB, colorC, colorD);
b.themeColors = themeColors;
return b;
}
case TYPE_ROUNDED: {
Border b = createRoundBorder(arcWidth, arcHeight, colorA);
b.themeColors = themeColors;
b.type = TYPE_ROUNDED_PRESSED;
return b;
}
case TYPE_ROUNDED_PRESSED: {
Border b = createRoundBorder(arcWidth, arcHeight, colorA);
b.themeColors = themeColors;
return b;
}
}
return this;
}
/**
* Has effect when the border demands responsibility for background painting
* normally the painter will perform this work but in this case the border might
* do it instead.
*
* @param g graphics context to draw onto
* @param c component whose border should be drawn
*/
public void paintBorderBackground(Graphics g, Component c) {
int x = c.getX();
int y = c.getY();
int width = c.getWidth();
int height = c.getHeight();
if (outerBorder != null) {
int ac;
if(millimeters) {
ac = Display.getInstance().convertToPixels(thickness);
} else {
ac = (int)thickness;
}
if (paintOuterBorderFirst) {
outerBorder.paintBorderBackground(g, c);
paintBorderBackground(g, x + ac, y + ac, width - ac * 2, height - ac * 2, c);
} else {
paintBorderBackground(g, x + ac, y + ac, width - ac * 2, height - ac * 2, c);
outerBorder.paintBorderBackground(g, c);
}
} else {
paintBorderBackground(g, x, y, width, height, c);
}
}
private void setClipScaled(Graphics g, int x, int y, int w, int h) {
if(g.getScaleX() < 1) {
w = (int)(((float)w) / g.getScaleX());
}
if(g.getScaleY() < 1) {
h = (int)(((float)h) / g.getScaleY());
}
g.setClip(x, y, w, h);
}
private void paintBorderBackground(Graphics g, final int xParameter, final int yParameter,
final int widthParameter, final int heightParameter, Component c) {
int originalColor = g.getColor();
int x = xParameter;
int y = yParameter;
int width = widthParameter;
int height = heightParameter;
switch(type) {
case TYPE_ROUNDED_PRESSED:
x++;
y++;
width -= 2;
height -= 2;
case TYPE_ROUNDED:
// Removing this due to issue 301, not sure regarding this...
//width--;
//height--;
// rounded is also responsible for drawing the background
Style s = c.getStyle();
if((s.getBgImage() != null && s.getBackgroundType() == Style.BACKGROUND_IMAGE_SCALED) ||
s.getBackgroundType() > 1) {
Object w = s.roundRectCache;
Image i = null;
if(w != null) {
i = (Image)Display.getInstance().extractHardRef(w);
}
if(i != null && i.getWidth() == width && i.getHeight() == height) {
g.drawImage(i, x, y);
} else {
// we need to draw a background image!
i = ImageFactory.createImage(c, width, height, 0);
Graphics imageG = i.getGraphics();
imageG.setColor(0);
imageG.fillRoundRect(0, 0, width, height, arcWidth, arcHeight);
int[] rgb = i.getRGBCached();
int transColor = rgb[0];
int[] imageRGB;
if(s.getBackgroundType() == Style.BACKGROUND_IMAGE_SCALED) {
imageRGB = s.getBgImage().scaled(width, height).getRGBCached();
} else {
Image bgPaint = ImageFactory.createImage(c, width, height, 0);
Painter p = s.getBgPainter();
// might occur during temporary conditions in the theme switching
if(p == null) {
return;
}
p.paint(bgPaint.getGraphics(), new Rectangle(0, 0, width, height));
imageRGB = bgPaint.getRGB();
}
int rlen = rgb.length;
for(int iter = 0 ; iter < rlen ; iter++) {
if(rgb[iter] == transColor) {
imageRGB[iter] = 0;
}
}
i = Image.createImage(imageRGB, width, height);
s.roundRectCache = Display.getInstance().createSoftWeakRef(i);
g.drawImage(i, x, y);
}
} else {
int foreground = g.getColor();
g.setColor(s.getBgColor());
// Its opaque much easier job!
if(s.getBgTransparency() == ((byte)0xff)) {
g.fillRoundRect(x, y , width, height, arcWidth, arcHeight);
} else {
if(g.isAlphaSupported()) {
int alpha = g.getAlpha();
g.setAlpha(s.getBgTransparency() & 0xff);
g.fillRoundRect(x, y , width, height, arcWidth, arcHeight);
g.setAlpha(alpha);
} else {
// if its transparent we don't need to do anything, if its
// translucent... well....
if(s.getBgTransparency() != 0) {
Image i = ImageFactory.createImage(c, width, height, 0);
int[] imageRgb;
if(g.getColor() != 0xffffff) {
Graphics imageG = i.getGraphics();
imageG.setColor(g.getColor());
imageG.fillRoundRect(0, 0 , width, height, arcWidth, arcHeight);
imageRgb = i.getRGBCached();
} else {
// background color is white we need to remove a different color
// black is the only other "reliable" color on the device
Graphics imageG = i.getGraphics();
imageG.setColor(0);
imageG.fillRect(0, 0, width, height);
imageG.setColor(g.getColor());
imageG.fillRoundRect(0, 0 , width, height, arcWidth, arcHeight);
imageRgb = i.getRGBCached();
}
int removeColor = imageRgb[0];
int size = width * height;
int alphaInt = ((s.getBgTransparency() & 0xff) << 24) & 0xff000000;
for(int iter = 0 ; iter < size ; iter++) {
if(removeColor == imageRgb[iter]) {
imageRgb[iter] = 0;
continue;
}
if((imageRgb[iter] & 0xff000000) != 0) {
imageRgb[iter] = (imageRgb[iter] & 0xffffff) | alphaInt;
}
}
g.drawImage(new RGBImage(imageRgb, width, height), x, y);
}
}
}
g.setColor(foreground);
}
break;
case TYPE_IMAGE: {
Image topLeft = images[4];
Image topRight = images[5];
Image bottomLeft = images[6];
Image center = images[8];
x += topLeft.getWidth();
y += topLeft.getHeight();
height -= (topLeft.getHeight() + bottomLeft.getHeight());
width -= (topLeft.getWidth() + topRight.getWidth());
if(center != null){
g.tileImage(center, x, y, width, height);
}
Image top = images[0]; Image bottom = images[1];
Image left = images[2]; Image right = images[3];
Image bottomRight = images[7];
x = xParameter;
y = yParameter;
width = widthParameter;
height = heightParameter;
g.drawImage(topLeft, x, y);
g.drawImage(bottomLeft, x, y + height - bottomLeft.getHeight());
g.drawImage(topRight, x + width - topRight.getWidth(), y);
g.drawImage(bottomRight, x + width - bottomRight.getWidth(), y + height - bottomRight.getHeight());
Image arrowDownImage = null;
Image arrowUpImage = null;
Image arrowLeftImage = null;
Image arrowRightImage = null;
int arrowPosition = 0;
// we need to draw an arrow on one of the sides
if(trackComponent != null && specialTile != null) {
int cabsY = c.getAbsoluteY();
int trackY = trackComponent.getY();
int trackX = trackComponent.getX();
int cabsX = c.getAbsoluteX();
if(cabsY >= trackY + trackComponent.getHeight()) {
// we are below the component
arrowUpImage = specialTile[0];
arrowPosition = (trackX + trackComponent.getWidth() / 2) - cabsX - arrowUpImage.getWidth() / 2;
} else {
if(cabsY + c.getHeight() <= trackY) {
// we are above the component
arrowDownImage = specialTile[1];
arrowPosition = (trackX + trackComponent.getWidth() / 2) - cabsX - arrowDownImage.getWidth() / 2;
} else {
if(cabsX >= trackX + trackComponent.getWidth()) {
// we are to the right of the component
arrowLeftImage = specialTile[2];
arrowPosition = (trackY + trackComponent.getHeight() / 2) - cabsY - arrowLeftImage.getHeight() / 2;
} else {
if(cabsX + c.getWidth() <= trackX) {
// we are to the left of the component
arrowRightImage = specialTile[3];
arrowPosition = (trackY + trackComponent.getHeight() / 2) - cabsY - arrowRightImage.getHeight() / 2;
}
}
}
}
}
drawImageBorderLine(g, topLeft, topRight, top, x, y, width, arrowUpImage, arrowPosition, false);
drawImageBorderLine(g, bottomLeft, bottomRight, bottom, x, y + height - bottom.getHeight(), width, arrowDownImage, arrowPosition, true);
drawImageBorderColumn(g, topLeft, bottomLeft, left, x, y, height, arrowLeftImage, arrowPosition, false);
drawImageBorderColumn(g, topRight, bottomRight, right, x + width - right.getWidth(), y, height, arrowRightImage, arrowPosition, true);
break;
}
case TYPE_IMAGE_SCALED: {
int clipX = g.getClipX();
int clipY = g.getClipY();
int clipWidth = g.getClipWidth();
int clipHeight = g.getClipHeight();
//g.pushClip();
Image topLeft = images[4];
Image topRight = images[5];
Image bottomLeft = images[6];
Image center = images[8];
x += topLeft.getWidth();
y += topLeft.getHeight();
height -= (topLeft.getHeight() + bottomLeft.getHeight());
width -= (topLeft.getWidth() + topRight.getWidth());
g.clipRect(x, y, width, height);
if(center != null && width > 0 && height > 0){
int centerWidth = center.getWidth();
int centerHeight = center.getHeight();
g.drawImage(center, x, y, width, height);
}
Image top = images[0]; Image bottom = images[1];
Image left = images[2]; Image right = images[3];
Image bottomRight = images[7];
g.setClip(clipX, clipY, clipWidth, clipHeight);
//g.popClip();
//g.pushClip();
x = xParameter;
y = yParameter;
width = widthParameter;
height = heightParameter;
g.drawImage(topLeft, x, y);
g.drawImage(bottomLeft, x, y + height - bottomLeft.getHeight());
g.drawImage(topRight, x + width - topRight.getWidth(), y);
g.drawImage(bottomRight, x + width - bottomRight.getWidth(), y + height - bottomRight.getHeight());
drawImageBorderLineScale(g, topLeft, topRight, top, x, y, width);
drawImageBorderLineScale(g, bottomLeft, bottomRight, bottom, x, y + height - bottom.getHeight(), width);
drawImageBorderColumnScale(g, topLeft, bottomLeft, left, x, y, height);
drawImageBorderColumnScale(g, topRight, bottomRight, right, x + width - right.getWidth(), y, height);
g.setClip(clipX, clipY, clipWidth, clipHeight);
//g.popClip();
break;
}
case TYPE_IMAGE_HORIZONTAL: {
Image left = images[0];
Image right = images[1];
Image center = images[2];
Boolean centerAlignHBorderBool = c == null ? null : (Boolean)c.getClientProperty("@centerAlignHBorderBool");
boolean b = centerAlignHBorderBool == null ? false : centerAlignHBorderBool;
if(b || c.getUIManager().isThemeConstant("centerAlignHBorderBool", false)) {
y += Math.max(0, height / 2 - center.getHeight() / 2);
}
g.drawImage(left, x, y);
g.drawImage(right, x + width - right.getWidth(), y);
g.tileImage(center, x + left.getWidth(), y, width - left.getWidth() - right.getWidth(), center.getHeight());
break;
}
case TYPE_IMAGE_VERTICAL: {
Image top = images[0];
Image bottom = images[1];
Image center = images[2];
g.drawImage(top, x, y);
g.drawImage(bottom, x, y + height - bottom.getHeight());
g.tileImage(center, x, y + top.getHeight(), center.getWidth(), height - top.getHeight() - bottom.getHeight());
break;
}
}
g.setColor(originalColor);
}
/**
* Draws the border for the given component, this method is called before a call to
* background painting is made.
*
* @param g graphics context to draw onto
* @param c component whose border should be drawn
*/
public void paint(Graphics g, Component c) {
int x = c.getX();
int y = c.getY();
int width = c.getWidth();
int height = c.getHeight();
if (outerBorder!=null) {
int ac;
if(millimeters) {
ac = Display.getInstance().convertToPixels(thickness);
} else {
ac = (int)thickness;
}
if(paintOuterBorderFirst) {
outerBorder.paint(g, x, y, width, height, c);
paint(g, x+ac, y+ac, width-ac*2, height-ac*2, c);
} else {
paint(g, x+ac, y+ac, width-ac*2, height-ac*2, c);
outerBorder.paint(g, x, y, width, height, c);
}
} else {
paint(g, x, y, width, height, c);
}
}
void paint(Graphics g,int x,int y,int width,int height,Component c) {
int originalColor = g.getColor();
if(!themeColors) {
g.setColor(colorA);
}
int ac = 1;
if(thickness > 0) {
if(millimeters) {
ac = Display.getInstance().convertToPixels(thickness);
} else {
ac = (int)thickness;
}
}
switch(type) {
case TYPE_LINE:
if (borderTitle==null) {
if(millimeters) {
g.drawRect(x, y, width, height, ac);
} else {
g.drawRect(x, y, width, height, ac);
}
} else {
Font f=c.getStyle().getFont();
int titleW=f.stringWidth(borderTitle);
int topPad=c.getStyle().getPaddingTop();
int topY=y+(topPad-ac)/2;
if (c.isRTL()) {
g.fillRect(x+width-TITLE_MARGIN, topY, TITLE_MARGIN , ac); //top (segment before the title)
g.fillRect(x, topY, width-(TITLE_MARGIN +titleW+TITLE_SPACE*2), ac); //top (segment after the title)
g.drawString(borderTitle, x+width-(TITLE_MARGIN +titleW+TITLE_SPACE), y+(topPad-f.getHeight())/2);
} else {
g.fillRect(x, topY, TITLE_MARGIN , ac); //top (segment before the title)
g.fillRect(x+TITLE_MARGIN +titleW+TITLE_SPACE*2, topY, width-(TITLE_MARGIN +titleW+TITLE_SPACE*2), ac); //top (segment after the title)
g.drawString(borderTitle, x+TITLE_MARGIN+TITLE_SPACE, y+(topPad-f.getHeight())/2);
}
g.fillRect(x, y+height-ac, width, ac); //bottom
g.fillRect(x, topY, ac, height); //left
g.fillRect(x+width-ac, topY, ac, height); //right
}
break;
case TYPE_DASHED:
case TYPE_DOTTED:
int segWidth=ac;
if (type==TYPE_DASHED) {
segWidth=ac*3;
}
int ix=x;
for (;ix=66)) {
g.drawRect(x + iter, y + iter, width, height);
}
width -= 2; height -= 2;
}
break;
case TYPE_INSET:
case TYPE_OUTSET:
for(int i=0;i 0) {
g.tileImage(center, x + left.getWidth(), y, width - right.getWidth() - left.getWidth(), center.getHeight());
if(arrow != null) {
imagePosition = Math.max(imagePosition, left.getWidth());
imagePosition = Math.min(imagePosition, width - arrow.getWidth() - right.getWidth());
if(farEdge) {
g.drawImage(arrow, x + imagePosition, y + center.getHeight() - arrow.getHeight());
} else {
g.drawImage(arrow, x + imagePosition, y);
}
}
}
}
private void drawImageBorderColumn(Graphics g, Image top, Image bottom, Image center, int x, final int y, int height, Image arrow, int imagePosition, boolean farEdge) {
if(height - bottom.getHeight() > 0) {
g.tileImage(center, x, y + top.getHeight(), center.getWidth(), height - top.getHeight() - bottom.getHeight());
if(arrow != null) {
imagePosition = Math.max(imagePosition, top.getHeight());
imagePosition = Math.min(imagePosition, height - arrow.getHeight() - bottom.getHeight());
if(farEdge) {
g.drawImage(arrow, x + center.getWidth() - arrow.getWidth(), y + imagePosition);
} else {
g.drawImage(arrow, x, y + imagePosition);
}
}
}
}
private void drawImageBorderLineScale(Graphics g, Image left, Image right, Image center, int x, int y, int width) {
int currentWidth = width - right.getWidth() - left.getWidth();
if(currentWidth > 0) {
x += left.getWidth();
g.drawImage(center, x, y, currentWidth, center.getHeight());
}
}
private void drawImageBorderColumnScale(Graphics g, Image top, Image bottom, Image center, int x, int y, int height) {
int currentHeight = height - bottom.getHeight() - top.getHeight();
if(currentHeight > 0) {
y += top.getHeight();
g.drawImage(center, x, y, center.getWidth(), currentHeight);
}
}
/**
* Sets the default border to the given value
*
* @param border new border value
*/
public static void setDefaultBorder(Border border) {
defaultBorder = border;
}
/**
* Gets the default border to the given value
*
* @return the default border
*/
public static Border getDefaultBorder() {
return defaultBorder;
}
/**
* This method returns how thick is the border in pixels, notice this doesn't apply to most border types
* @return the Border thickness
*/
public int getThickness() {
if(millimeters) {
return Display.getInstance().convertToPixels(thickness);
}
return (int)thickness;
}
/**
* This method returns sets the border thickness in pixels, notice this doesn't apply to most border types
* @param thickness the Border thickness
*/
public void setThickness(int thickness) {
this.thickness = thickness;
}
/**
* Allows toggling the order in which the outer and inner borders are painted for the Outer border type
* @param paintOuterBorderFirst whether the outside border should be painter first
*/
public void setPaintOuterBorderFirst(boolean paintOuterBorderFirst) {
this.paintOuterBorderFirst = paintOuterBorderFirst;
}
/**
* Allows toggling the order in which the outer and inner borders are painted for the Outer border type
* @return whether the outside border should be painter first
*/
public boolean isPaintOuterBorderFirst() {
return paintOuterBorderFirst;
}
/**
* This method returns the Compound Borders array.
* The array size is 4 and the borders arranged as follows :
* Border[] borders = getCompoundBorders();
* Border top = borders[Component.TOP];
* Border bottom = borders[Component.BOTTOM];
* Border left = borders[Component.LEFT];
* Border right = borders[Component.RIGHT];
*
* @return the borders array or null if this is not a Compound Border
*/
public Border[] getCompoundBorders() {
return compoundBorders;
}
/**
* This callback indicates that a component pointing at this border is initialized, this
* method is useful for image borders whose lock methods are implicitly invoked.
* This method may be invoked multiple times.
*/
public void lock() {
if(images != null) {
int ilen = images.length;
for(int iter = 0 ; iter < ilen ; iter++) {
if(images[iter] != null) {
images[iter].lock();
}
}
}
}
/**
* This callback indicates that a component pointing at this border is now deinitilized
* This method may be invoked multiple times.
*/
public void unlock() {
if(images != null) {
int ilen = images.length;
for(int iter = 0 ; iter < ilen ; iter++) {
if(images[iter] != null) {
images[iter].unlock();
}
}
}
}
/**
* This method is for internal usage only!
*/
public Object getProperty(String n) {
if(n.equals("ThemeColors")) {
if(themeColors) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
if(n.equals("Type")) {
return new Integer(type);
}
if(n.equals("ColorA")) {
return new Integer(colorA);
}
if(n.equals("ColorB")) {
return new Integer(colorB);
}
if(n.equals("ColorC")) {
return new Integer(colorC);
}
if(n.equals("ColorD")) {
return new Integer(colorD);
}
if(n.equals("ArcWidth")) {
return new Integer(arcWidth);
}
if(n.equals("ArcHeight")) {
return new Integer(arcHeight);
}
if(n.equals("Images")) {
return images;
}
return null;
}
}