mirror of https://github.com/FreeCol/freecol.git
576 lines
21 KiB
Java
576 lines
21 KiB
Java
/**
|
|
* Copyright (C) 2002-2022 The FreeCol Team
|
|
*
|
|
* This file is part of FreeCol.
|
|
*
|
|
* FreeCol is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* FreeCol 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package net.sf.freecol.client.gui.panel;
|
|
|
|
import static net.sf.freecol.common.util.ImageUtils.fillTexture;
|
|
|
|
import java.awt.Component;
|
|
import java.awt.Dimension;
|
|
import java.awt.Graphics;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.Image;
|
|
import java.awt.Insets;
|
|
import java.awt.Rectangle;
|
|
import java.awt.image.BufferedImage;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import javax.swing.border.AbstractBorder;
|
|
|
|
import net.sf.freecol.common.resources.ImageResource;
|
|
import net.sf.freecol.common.resources.ResourceManager;
|
|
|
|
|
|
/**
|
|
* A border created from a set of images.
|
|
*/
|
|
public class FreeColImageBorder extends AbstractBorder {
|
|
|
|
private static final List<FreeColImageBorder> borders = new ArrayList<>();
|
|
|
|
public static final FreeColImageBorder panelWithoutShadowBorder = new FreeColImageBorder("image.border.panel.noshadow");
|
|
public static final FreeColImageBorder panelBorder = new FreeColImageBorder("image.border.panel");
|
|
public static final FreeColImageBorder buttonBorder = new FreeColImageBorder("image.border.button");
|
|
public static final FreeColImageBorder simpleButtonBorder = new FreeColImageBorder("image.border.button.simple");
|
|
public static final FreeColImageBorder menuBarBorder = new FreeColImageBorder("image.border.menu");
|
|
public static final FreeColImageBorder woodenPanelBorder = new FreeColImageBorder("image.border.wooden");
|
|
|
|
public static final FreeColImageBorder colonyWarehouseBorder = new FreeColImageBorder("image.border.colonyWarehouse");
|
|
public static final FreeColImageBorder colonyPanelBorder = new FreeColImageBorder("image.border.colony.panel");
|
|
public static final FreeColImageBorder innerColonyPanelBorder = new FreeColImageBorder("image.border.colony.panel.inner");
|
|
public static final FreeColImageBorder outerColonyPanelBorder = new FreeColImageBorder("image.border.colony.panel.outer");
|
|
|
|
private static float scaleFactor = 1;
|
|
|
|
/**
|
|
* The key used for getting the image resources.
|
|
*/
|
|
private final String baseKey;
|
|
|
|
/**
|
|
* If true, no scaling to the border is applied.
|
|
*/
|
|
private final boolean noScaling;
|
|
|
|
private boolean initialized = false;
|
|
|
|
/**
|
|
* NW-corner
|
|
*/
|
|
private BufferedImage topLeftCornerImage;
|
|
|
|
/**
|
|
* NNW
|
|
*/
|
|
private BufferedImage topStartImage;
|
|
|
|
/**
|
|
* N-border
|
|
*/
|
|
private BufferedImage topImage;
|
|
|
|
/**
|
|
* NNE
|
|
*/
|
|
private BufferedImage topEndImage;
|
|
|
|
/**
|
|
* NE-corner
|
|
*/
|
|
private BufferedImage topRightCornerImage;
|
|
|
|
/**
|
|
* ENE
|
|
*/
|
|
private BufferedImage rightStartImage;
|
|
|
|
/**
|
|
* E-border
|
|
*/
|
|
private BufferedImage rightImage;
|
|
|
|
/**
|
|
* ESE
|
|
*/
|
|
private BufferedImage rightEndImage;
|
|
|
|
/**
|
|
* SE-corner
|
|
*/
|
|
private BufferedImage bottomRightCornerImage;
|
|
|
|
/**
|
|
* SSE
|
|
*/
|
|
private BufferedImage bottomEndImage;
|
|
|
|
/**
|
|
* S-border
|
|
*/
|
|
private BufferedImage bottomImage;
|
|
|
|
/**
|
|
* SSW
|
|
*/
|
|
private BufferedImage bottomStartImage;
|
|
|
|
/**
|
|
* SW-corner
|
|
*/
|
|
private BufferedImage bottomLeftCornerImage;
|
|
|
|
/**
|
|
* WSW
|
|
*/
|
|
private BufferedImage leftEndImage;
|
|
|
|
/**
|
|
* W-border
|
|
*/
|
|
private BufferedImage leftImage;
|
|
|
|
/**
|
|
* WNW
|
|
*/
|
|
private BufferedImage leftStartImage;
|
|
|
|
|
|
private FreeColImageBorder(String baseKey) {
|
|
this(baseKey, false);
|
|
}
|
|
|
|
|
|
private FreeColImageBorder(String baseKey, boolean noScaling) {
|
|
this.baseKey = baseKey;
|
|
this.noScaling = noScaling;
|
|
|
|
loadImages();
|
|
borders.add(this);
|
|
}
|
|
|
|
|
|
private void ensureInitialized() {
|
|
if (!initialized) {
|
|
loadImages();
|
|
}
|
|
}
|
|
|
|
private void loadImages() {
|
|
loadImages(getImage(baseKey + ".nw"),
|
|
getImage(baseKey + ".nnw"),
|
|
getImage(baseKey + ".n"),
|
|
getImage(baseKey + ".nne"),
|
|
getImage(baseKey + ".ne"),
|
|
getImage(baseKey + ".ene"),
|
|
getImage(baseKey + ".e"),
|
|
getImage(baseKey + ".ese"),
|
|
getImage(baseKey + ".se"),
|
|
getImage(baseKey + ".sse"),
|
|
getImage(baseKey + ".s"),
|
|
getImage(baseKey + ".ssw"),
|
|
getImage(baseKey + ".sw"),
|
|
getImage(baseKey + ".wsw"),
|
|
getImage(baseKey + ".w"),
|
|
getImage(baseKey + ".wnw"));
|
|
initialized = true;
|
|
}
|
|
|
|
/**
|
|
* Loads the images for the border.
|
|
*
|
|
* @param topLeftCornerImage NW-corner
|
|
* @param topImage N-border
|
|
* @param topRightCornerImage NE-corner
|
|
* @param rightImage E-border
|
|
* @param bottomRightCornerImage SE-corner
|
|
* @param bottomImage S-border
|
|
* @param bottomLeftCornerImage SW-corner
|
|
* @param leftImage W-border
|
|
*/
|
|
private void loadImages(BufferedImage topLeftCornerImage,
|
|
BufferedImage topStartImage,
|
|
BufferedImage topImage,
|
|
BufferedImage topEndImage,
|
|
BufferedImage topRightCornerImage,
|
|
BufferedImage rightStartImage,
|
|
BufferedImage rightImage,
|
|
BufferedImage rightEndImage,
|
|
BufferedImage bottomRightCornerImage,
|
|
BufferedImage bottomEndImage,
|
|
BufferedImage bottomImage,
|
|
BufferedImage bottomStartImage,
|
|
BufferedImage bottomLeftCornerImage,
|
|
BufferedImage leftEndImage,
|
|
BufferedImage leftImage,
|
|
BufferedImage leftStartImage) {
|
|
this.topLeftCornerImage = topLeftCornerImage;
|
|
this.topStartImage = topStartImage;
|
|
this.topImage = topImage;
|
|
this.topEndImage = topEndImage;
|
|
this.topRightCornerImage = topRightCornerImage;
|
|
this.rightStartImage = rightStartImage;
|
|
this.rightImage = rightImage;
|
|
this.rightEndImage = rightEndImage;
|
|
this.bottomRightCornerImage = bottomRightCornerImage;
|
|
this.bottomEndImage = bottomEndImage;
|
|
this.bottomImage = bottomImage;
|
|
this.bottomStartImage = bottomStartImage;
|
|
this.bottomLeftCornerImage = bottomLeftCornerImage;
|
|
this.leftEndImage = leftEndImage;
|
|
this.leftImage = leftImage;
|
|
this.leftStartImage = leftStartImage;
|
|
}
|
|
|
|
public static void setScaleFactor(float scaleFactor) {
|
|
FreeColImageBorder.scaleFactor = scaleFactor;
|
|
reloadAllImages();
|
|
}
|
|
|
|
private static void reloadAllImages() {
|
|
borders.stream().forEach(FreeColImageBorder::loadImages);
|
|
}
|
|
|
|
|
|
private BufferedImage getImage(String key) {
|
|
final ImageResource ir = ResourceManager.getImageResource(key, false);
|
|
if (ir == null) {
|
|
return null;
|
|
}
|
|
final BufferedImage image = ir.getImage();
|
|
if (noScaling) {
|
|
return image;
|
|
}
|
|
final Dimension scaledDimensions = new Dimension(
|
|
(int) Math.round(image.getWidth() * scaleFactor),
|
|
(int) Math.round(image.getHeight() * scaleFactor)
|
|
);
|
|
return ir.getImage(scaledDimensions, false);
|
|
}
|
|
|
|
/**
|
|
* Gets the insets of this border around the given component.
|
|
*
|
|
* @param c The {@code Component} having the border.
|
|
* @return The {@code Insets}.
|
|
*/
|
|
@Override
|
|
public Insets getBorderInsets(Component c) {
|
|
ensureInitialized();
|
|
return getBorderInsets(c, null);
|
|
}
|
|
|
|
/**
|
|
* Returns spaces that are open on this border.
|
|
*
|
|
* @param c The component having the border.
|
|
* @return A list of areas that can be considered out-of-bounds for this border.
|
|
*/
|
|
public List<Rectangle> getOpenSpace(Component c) {
|
|
ensureInitialized();
|
|
|
|
final List<Rectangle> openSpace = new ArrayList<>();
|
|
|
|
/*
|
|
* For now, only the top part of the border is handled. Feel free to extend this method.
|
|
*/
|
|
|
|
if (topStartImage != null && topStartImage.getHeight() < max(getHeight(topImage), getHeight(topEndImage))) {
|
|
final int openSpaceHeight = max(getHeight(topImage), getHeight(topEndImage)) - topStartImage.getHeight();
|
|
openSpace.add(new Rectangle(0, 0, topStartImage.getWidth(), openSpaceHeight));
|
|
}
|
|
if (topImage != null && topImage.getHeight() < max(getHeight(topStartImage), getHeight(topEndImage))) {
|
|
final int openSpaceHeight = max(getHeight(topStartImage), getHeight(topEndImage)) - topImage.getHeight();
|
|
final int x = getWidth(topStartImage);
|
|
final int width = c.getWidth() - getWidth(topStartImage) - getWidth(topEndImage);
|
|
openSpace.add(new Rectangle(x, 0, width, openSpaceHeight));
|
|
}
|
|
if (topEndImage != null && topEndImage.getHeight() < max(getHeight(topStartImage), getHeight(topImage))) {
|
|
final int openSpaceHeight = max(getHeight(topStartImage), getHeight(topImage)) - topEndImage.getHeight();
|
|
openSpace.add(new Rectangle(c.getWidth() - topEndImage.getWidth(), 0, topEndImage.getWidth(), openSpaceHeight));
|
|
}
|
|
|
|
return openSpace;
|
|
}
|
|
|
|
/**
|
|
* Gets the insets of this border around the given component.
|
|
*
|
|
* @param c The {@code Component} having the border.
|
|
* @param insets An instance of {@code Insets} to be updated.
|
|
* @return The given instance of {@code Insets} if not
|
|
* {@code null}, or a new instance otherwise.
|
|
*/
|
|
@Override
|
|
public Insets getBorderInsets(Component c, Insets insets) {
|
|
ensureInitialized();
|
|
int top = max(
|
|
getHeight(topImage),
|
|
getHeight(topStartImage),
|
|
getHeight(topEndImage),
|
|
getHeight(topLeftCornerImage),
|
|
getHeight(topRightCornerImage)
|
|
);
|
|
int left = max(
|
|
getWidth(leftImage),
|
|
getWidth(leftStartImage),
|
|
getWidth(leftEndImage),
|
|
getWidth(topLeftCornerImage),
|
|
getWidth(bottomLeftCornerImage)
|
|
);
|
|
int bottom = max(
|
|
getHeight(bottomImage),
|
|
getHeight(bottomStartImage),
|
|
getHeight(bottomEndImage),
|
|
getHeight(bottomLeftCornerImage),
|
|
getHeight(bottomRightCornerImage)
|
|
);
|
|
int right = max(
|
|
getWidth(rightImage),
|
|
getWidth(rightStartImage),
|
|
getWidth(rightEndImage),
|
|
getWidth(topRightCornerImage),
|
|
getWidth(bottomRightCornerImage)
|
|
);
|
|
|
|
if (topImage == null) {
|
|
top = 0;
|
|
}
|
|
if (leftImage == null) {
|
|
left = 0;
|
|
}
|
|
if (bottomImage == null) {
|
|
bottom = 0;
|
|
}
|
|
if (rightImage == null) {
|
|
right = 0;
|
|
}
|
|
|
|
if (insets == null) {
|
|
return new Insets(top, left, bottom, right);
|
|
} else {
|
|
insets.top = top;
|
|
insets.left = left;
|
|
insets.bottom = bottom;
|
|
insets.right = right;
|
|
return insets;
|
|
}
|
|
}
|
|
|
|
private int max(int... numbers) {
|
|
int highest = Integer.MIN_VALUE;
|
|
for (int i : numbers) {
|
|
if (i > highest) {
|
|
highest = i;
|
|
}
|
|
}
|
|
return highest;
|
|
}
|
|
|
|
/**
|
|
* Paints the border on the given component.
|
|
*
|
|
* @param c The {@code Component} to draw the border on.
|
|
* @param g The {@code Graphics} used for painting the border.
|
|
* @param x The x-component of the offset.
|
|
* @param y The y-component of the offset.
|
|
* @param width The width of the border.
|
|
* @param height The height of the border.
|
|
*/
|
|
@Override
|
|
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
|
ensureInitialized();
|
|
|
|
final Insets insets = getBorderInsets(c);
|
|
final Graphics2D g2 = (Graphics2D) g;
|
|
|
|
// Get width and height of the images
|
|
final int topHeight = getHeight(topImage);
|
|
final int leftWidth = getWidth(leftImage);
|
|
final int bottomHeight = getHeight(bottomImage);
|
|
final int rightWidth = getWidth(rightImage);
|
|
|
|
final int topStartWidth = getWidth(topStartImage);
|
|
final int topStartHeight = getHeight(topStartImage);
|
|
final int topEndWidth = getWidth(topEndImage);
|
|
final int topEndHeight = getHeight(topEndImage);
|
|
final int leftStartWidth = getWidth(leftStartImage);
|
|
final int leftStartHeight = getHeight(leftStartImage);
|
|
final int leftEndWidth = getWidth(leftEndImage);
|
|
final int leftEndHeight = getHeight(leftEndImage);
|
|
final int bottomStartWidth = getWidth(bottomStartImage);
|
|
final int bottomStartHeight = getHeight(bottomStartImage);
|
|
final int bottomEndWidth = getWidth(bottomEndImage);
|
|
final int bottomEndHeight = getHeight(bottomEndImage);
|
|
final int rightStartWidth = getWidth(rightStartImage);
|
|
final int rightStartHeight = getHeight(rightStartImage);
|
|
final int rightEndWidth = getWidth(rightEndImage);
|
|
final int rightEndHeight = getHeight(rightEndImage);
|
|
|
|
final int topLeftCornerWidth = getWidth(topLeftCornerImage);
|
|
final int topLeftCornerHeight = getHeight(topLeftCornerImage);
|
|
final int topRightCornerWidth = getWidth(topRightCornerImage);
|
|
final int topRightCornerHeight = getHeight(topRightCornerImage);
|
|
final int bottomLeftCornerWidth = getWidth(bottomLeftCornerImage);
|
|
final int bottomLeftCornerHeight = getHeight(bottomLeftCornerImage);
|
|
final int bottomRightCornerWidth = getWidth(bottomRightCornerImage);
|
|
final int bottomRightCornerHeight = getHeight(bottomRightCornerImage);
|
|
|
|
// Add the image border
|
|
if (topStartImage != null) {
|
|
final int w = Math.min(topStartWidth, width - topLeftCornerWidth - topRightCornerWidth);
|
|
fillTexture(g2, topStartImage,
|
|
x + topLeftCornerWidth,
|
|
y + insets.top - topStartHeight,
|
|
w,
|
|
topStartHeight);
|
|
}
|
|
if (topImage != null) {
|
|
fillTexture(g2, topImage,
|
|
x + topLeftCornerWidth + topStartWidth,
|
|
y + insets.top - topHeight,
|
|
width - topLeftCornerWidth - topRightCornerWidth - topStartWidth - topEndWidth,
|
|
topHeight);
|
|
}
|
|
if (topEndImage != null) {
|
|
final int w = Math.min(topEndWidth, width - topLeftCornerWidth - topRightCornerWidth - topStartWidth);
|
|
fillTexture(g2, topEndImage,
|
|
x + width - Math.max(insets.right, topRightCornerWidth) - w,
|
|
y + insets.top - topEndHeight,
|
|
w,
|
|
topEndHeight);
|
|
}
|
|
if (leftStartImage != null) {
|
|
final int h = Math.min(leftStartHeight, height - Math.max(insets.top, topLeftCornerHeight) - bottomLeftCornerHeight);
|
|
fillTexture(g2, leftStartImage,
|
|
x + insets.left - leftStartWidth,
|
|
y + Math.max(insets.top, topLeftCornerHeight),
|
|
leftStartWidth,
|
|
h);
|
|
}
|
|
if (leftImage != null) {
|
|
fillTexture(g2, leftImage,
|
|
x + insets.left - leftWidth,
|
|
y + Math.max(insets.top, topLeftCornerHeight) + leftStartHeight,
|
|
leftWidth,
|
|
height - Math.max(insets.top, topLeftCornerHeight) - Math.max(insets.bottom, bottomLeftCornerHeight) - leftStartHeight - leftEndHeight);
|
|
}
|
|
if (leftEndImage != null) {
|
|
final int h = Math.min(leftEndHeight, height - topLeftCornerHeight - bottomLeftCornerHeight - leftStartHeight);
|
|
fillTexture(g2, leftEndImage,
|
|
x + insets.left - leftEndWidth,
|
|
y + height - Math.max(insets.bottom, bottomLeftCornerHeight) - h,
|
|
leftEndWidth,
|
|
h);
|
|
}
|
|
if (bottomStartImage != null) {
|
|
final int w = Math.min(bottomStartWidth, width - bottomLeftCornerWidth - bottomRightCornerWidth);
|
|
fillTexture(g2, bottomStartImage,
|
|
x + bottomLeftCornerWidth,
|
|
y + height - insets.bottom,
|
|
w,
|
|
bottomStartHeight);
|
|
}
|
|
if (bottomImage != null) {
|
|
fillTexture(g2, bottomImage,
|
|
x + bottomLeftCornerWidth + bottomStartWidth,
|
|
y + height - insets.bottom,
|
|
width - bottomLeftCornerWidth - bottomRightCornerWidth - bottomStartWidth - bottomEndWidth,
|
|
bottomHeight);
|
|
}
|
|
if (bottomEndImage != null) {
|
|
final int w = Math.min(bottomEndWidth, width - bottomLeftCornerWidth - bottomRightCornerWidth - bottomStartWidth);
|
|
fillTexture(g2, bottomEndImage,
|
|
x + width - Math.max(insets.right, bottomRightCornerWidth) - w,
|
|
y + height - insets.bottom,
|
|
w,
|
|
bottomEndHeight);
|
|
}
|
|
if (rightStartImage != null) {
|
|
final int h = Math.min(rightStartHeight, height - Math.max(insets.top, topRightCornerHeight) - bottomRightCornerHeight);
|
|
fillTexture(g2, rightStartImage,
|
|
x + width - insets.right,
|
|
y + Math.max(insets.top, topRightCornerHeight),
|
|
rightStartWidth,
|
|
h);
|
|
}
|
|
if (rightImage != null) {
|
|
fillTexture(g2, rightImage,
|
|
x + width - insets.right,
|
|
y + Math.max(insets.top, topRightCornerHeight) + rightStartHeight,
|
|
rightWidth,
|
|
height - Math.max(insets.top, topRightCornerHeight) - bottomRightCornerHeight - rightStartHeight - rightEndHeight);
|
|
}
|
|
if (rightEndImage != null) {
|
|
final int h = Math.min(rightEndHeight, height - topRightCornerHeight - bottomRightCornerHeight - rightStartHeight);
|
|
fillTexture(g2, rightEndImage,
|
|
x + width - insets.right,
|
|
y + height - Math.max(insets.bottom, bottomRightCornerHeight) - h,
|
|
rightEndWidth,
|
|
h);
|
|
}
|
|
if (topLeftCornerImage != null) {
|
|
fillTexture(g2, topLeftCornerImage,
|
|
x + Math.max(insets.left, topLeftCornerWidth) - topLeftCornerWidth,
|
|
y + Math.max(insets.top, topLeftCornerHeight) - topLeftCornerHeight,
|
|
topLeftCornerWidth,
|
|
topLeftCornerHeight);
|
|
}
|
|
if (topRightCornerImage != null) {
|
|
fillTexture(g2, topRightCornerImage,
|
|
x + width - Math.max(insets.right, topRightCornerWidth),
|
|
y + Math.max(insets.top, topRightCornerHeight) - topRightCornerHeight,
|
|
topRightCornerWidth,
|
|
topRightCornerHeight);
|
|
}
|
|
if (bottomLeftCornerImage != null) {
|
|
fillTexture(g2, bottomLeftCornerImage,
|
|
x + Math.max(insets.left, bottomLeftCornerWidth) - bottomLeftCornerWidth,
|
|
y + height - Math.max(insets.bottom, bottomLeftCornerHeight),
|
|
bottomLeftCornerWidth,
|
|
bottomLeftCornerHeight);
|
|
}
|
|
if (bottomRightCornerImage != null) {
|
|
fillTexture(g2, bottomRightCornerImage,
|
|
x + width - Math.max(insets.right, bottomRightCornerWidth),
|
|
y + height - Math.max(insets.bottom, bottomRightCornerHeight),
|
|
bottomRightCornerWidth,
|
|
bottomRightCornerHeight);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the height of an image. If image is null, return 0.
|
|
* @param im The image.
|
|
* @return The height of the image.
|
|
*/
|
|
private static int getHeight(Image im) {
|
|
return (im == null) ? 0 : im.getHeight(null);
|
|
}
|
|
|
|
/**
|
|
* Get the width of an image. If image is null, return 0.
|
|
* @param im The image.
|
|
* @return The width of the image.
|
|
*/
|
|
private static int getWidth(Image im) {
|
|
return (im == null) ? 0 : im.getWidth(null);
|
|
}
|
|
}
|