From 19f71137d9f679a251fc689baf9107856816c831 Mon Sep 17 00:00:00 2001 From: Stian Grenborgen Date: Mon, 1 Jan 2024 21:31:20 +0100 Subject: [PATCH] Massive redesign of the colony panel with background images and other styling. Buildings are now randomly distributed in the colony based on the available space (this is annoying when resizing the colony panel, but otherwise really good). The sizes of various panels have been tweaked to work both on small and large displays ... and panels that previously often required scrollbars have now been repositioned and made bigger. --- data/base/resources.properties | 70 +++ .../sf/freecol/client/gui/ImageLibrary.java | 50 +++ .../client/gui/panel/BuildingPanel.java | 22 +- .../gui/panel/BuildingsLayoutManager.java | 313 +++++++++++++ .../freecol/client/gui/panel/CargoPanel.java | 43 +- .../freecol/client/gui/panel/ColonyPanel.java | 421 ++++++++++++++---- .../freecol/client/gui/panel/EuropePanel.java | 2 +- .../client/gui/panel/FreeColImageBorder.java | 5 +- 8 files changed, 820 insertions(+), 106 deletions(-) create mode 100644 src/net/sf/freecol/client/gui/panel/BuildingsLayoutManager.java diff --git a/data/base/resources.properties b/data/base/resources.properties index 4a13adc98f..3e28653bf0 100644 --- a/data/base/resources.properties +++ b/data/base/resources.properties @@ -36,6 +36,7 @@ image.background.FreeColButton=resources/images/ui/bg_button.png image.background.MainPanel=resources/images/ui/bg_brown.png image.background.AboutPanel=resources/images/ui/bg_brown.png image.background.ColopediaPanel=resources/images/ui/bg_brown.png +image.background.ColonyPanel=resources/images/ui/bg_paper_brown.png image.background.FreeColList=resource:image.background.Paper image.background.FreeColMenuBar=resources/images/ui/bg_menubar.png @@ -48,6 +49,13 @@ image.background.FreeColTextArea=resource:image.background.FreeColBrightPanel image.background.FreeColTextField=resource:image.background.FreeColBrightPanel image.background.FreeColToolTip=resource:image.background.Paper +image.cargohold.available=resources/images/ui/cargohold.png +image.cargohold.unavailable=resources/images/ui/cargohold-unavailable.png +image.colony.docks.background=resources/images/ui/colonydocks.png +image.colony.docks.sky.background=resources/images/ui/colonydocks-sky.png +image.colony.warehouse.background=resources/images/ui/warehouse-bg.png +image.colony.upperRight.background=resources/images/ui/colony-upper-right-bg.png + # Borders #image.border.menu.s=resource:image.empty image.border.menu.s=resources/images/ui/menuborder.png @@ -88,6 +96,68 @@ image.border.button.simple.ne=resources/images/ui/simplebuttonborder-ne.png image.border.button.simple.sw=resources/images/ui/simplebuttonborder-sw.png image.border.button.simple.se=resources/images/ui/simplebuttonborder-se.png +image.border.wooden.n=resources/images/ui/border/wooden/woodenborder-n.png +image.border.wooden.w=resources/images/ui/border/wooden/woodenborder-w.png +image.border.wooden.e=resources/images/ui/border/wooden/woodenborder-e.png +image.border.wooden.s=resources/images/ui/border/wooden/woodenborder-s.png +image.border.wooden.nw=resources/images/ui/border/wooden/woodenborder-nw.png +image.border.wooden.ne=resources/images/ui/border/wooden/woodenborder-ne.png +image.border.wooden.sw=resources/images/ui/border/wooden/woodenborder-sw.png +image.border.wooden.se=resources/images/ui/border/wooden/woodenborder-se.png + +image.border.colonyWarehouse.n=resources/images/ui/border/wooden/woodenborder-n.png +#image.border.colonyWarehouse.w=resources/images/ui/border/wooden/woodenborder-w.png +image.border.colonyWarehouse.e=resources/images/ui/border/wooden/woodenborder-e.png +#image.border.colonyWarehouse.s=resources/images/ui/border/wooden/woodenborder-s.png +image.border.colonyWarehouse.nw=resources/images/ui/border/wooden/woodenborder-nw.png +image.border.colonyWarehouse.ne=resources/images/ui/border/wooden/woodenborder-ne.png +#image.border.colonyWarehouse.sw=resources/images/ui/border/wooden/woodenborder-sw.png +#image.border.colonyWarehouse.se=resources/images/ui/border/wooden/woodenborder-se.png + +image.border.colony.panel.n=resources/images/ui/border/carvedwood/carvedwoodenborder-n.png +image.border.colony.panel.w=resources/images/ui/border/carvedwood/carvedwoodenborder-w.png +image.border.colony.panel.e=resources/images/ui/border/carvedwood/carvedwoodenborder-e.png +image.border.colony.panel.s=resources/images/ui/border/carvedwood/carvedwoodenborder-s.png +image.border.colony.panel.nw=resources/images/ui/border/carvedwood/carvedwoodenborder-nw.png +image.border.colony.panel.ne=resources/images/ui/border/carvedwood/carvedwoodenborder-ne.png +image.border.colony.panel.sw=resources/images/ui/border/carvedwood/carvedwoodenborder-sw.png +image.border.colony.panel.se=resources/images/ui/border/carvedwood/carvedwoodenborder-se.png + +image.border.colony.panel.inner.n=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-n.png +image.border.colony.panel.inner.w=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-w.png +image.border.colony.panel.inner.e=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-e.png +image.border.colony.panel.inner.s=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-s.png +image.border.colony.panel.inner.nw=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-nw.png +#image.border.colony.panel.inner.ne=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-ne.png +image.border.colony.panel.inner.sw=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-sw.png +image.border.colony.panel.inner.se=resources/images/ui/border/carvedwood/carvedwoodenborder-inner-se.png + +image.border.colony.panel.inner.ne=resources/images/ui/border/carvedwood/carvedwood-colony-inner-ne.png +image.border.colony.panel.inner.nne=resources/images/ui/border/carvedwood/carvedwood-colony-inner-nne.png +image.border.colony.panel.inner.ene=resources/images/ui/border/carvedwood/carvedwood-colony-inner-ene.png + + +image.border.colony.panel.outer.n=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-n.png +image.border.colony.panel.outer.w=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-w.png +image.border.colony.panel.outer.e=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-e.png +image.border.colony.panel.outer.s=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-s.png +image.border.colony.panel.outer.nw=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-nw.png +#image.border.colony.panel.outer.ne=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-ne.png +image.border.colony.panel.outer.sw=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-sw.png +image.border.colony.panel.outer.se=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-se.png +image.border.colony.panel.outer.nnw=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-nnw.png +#image.border.colony.panel.outer.nne=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-nne.png +#image.border.colony.panel.outer.ene=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-ene.png +image.border.colony.panel.outer.ese=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-ese.png +image.border.colony.panel.outer.sse=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-sse.png +image.border.colony.panel.outer.ssw=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-ssw.png +image.border.colony.panel.outer.wsw=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-wsw.png +image.border.colony.panel.outer.wnw=resources/images/ui/border/carvedwood/carvedwoodenborder-outer-wnw.png +image.border.colony.panel.outer.ne=resources/images/ui/border/carvedwood/carvedwood-colony-outer-ne.png +image.border.colony.panel.outer.nne=resources/images/ui/border/carvedwood/carvedwood-colony-outer-nne.png +image.border.colony.panel.outer.ene=resources/images/ui/border/carvedwood/carvedwood-colony-outer-ene.png + + # Interface image.ui.button.radio=resources/images/ui/button-radio.png image.ui.button.radio.selected=resources/images/ui/button-radio-selected.png diff --git a/src/net/sf/freecol/client/gui/ImageLibrary.java b/src/net/sf/freecol/client/gui/ImageLibrary.java index b05cd9e9e6..ce1966fcb3 100644 --- a/src/net/sf/freecol/client/gui/ImageLibrary.java +++ b/src/net/sf/freecol/client/gui/ImageLibrary.java @@ -821,6 +821,36 @@ public BufferedImage getScaledBuildingTypeImage(BuildingType buildingType, final String key = getBuildingTypeKey(buildingType); return this.imageCache.getScaledImage(key, scale, false); } + + public BufferedImage getScaledCargoHold(boolean available) { + final String key = "image.cargohold." + (available ? "available" : "unavailable"); + return this.imageCache.getScaledImage(key, this.scaleFactor, false); + } + + public BufferedImage getColonyDocks() { + final String key = "image.colony.docks.background"; + return this.imageCache.getScaledImage(key, this.scaleFactor, false); + } + + public BufferedImage getColonyDocksSky() { + final String key = "image.colony.docks.sky.background"; + return this.imageCache.getScaledImage(key, this.scaleFactor, false); + } + + public BufferedImage getColonyUpperRightBackground() { + final String key = "image.colony.upperRight.background"; + return this.imageCache.getScaledImage(key, this.scaleFactor, false); + } + + public BufferedImage getColonyWarehouseBackground() { + final String key = "image.colony.warehouse.background"; + return this.imageCache.getScaledImage(key, this.scaleFactor, false); + } + + public BufferedImage getScaledBuildingEmptyLandImage() { + final String key = "image.buildingicon.model.building.BuildingSite"; + return this.imageCache.getScaledImage(key, this.scaleFactor, false); + } private BufferedImage getScaledBuildingTypeImage(BuildingType buildingType, Player player, @@ -832,6 +862,26 @@ private BufferedImage getScaledBuildingTypeImage(BuildingType buildingType, } return this.imageCache.getScaledImage(key, scale, false); } + + public Dimension determineMaxSizeUsingSizeFromAllLevels(BuildingType buildingType, Player player) { + int maxWidth = 0; + int maxHeight = 0; + while (buildingType.getUpgradesFrom() != null) { + buildingType = buildingType.getUpgradesFrom(); + } + do { + final Image buildingImage = getScaledBuildingTypeImage(buildingType, player, getScaleFactor()); + if (buildingImage.getWidth(null) > maxWidth) { + maxWidth = buildingImage.getWidth(null); + } + if (buildingImage.getHeight(null) > maxHeight) { + maxHeight = buildingImage.getHeight(null); + } + buildingType = buildingType.getUpgradesTo(); + } while (buildingType != null); + + return new Dimension(maxWidth, maxHeight); + } public BufferedImage getScaledBuildingImage(Building building) { return getScaledBuildingTypeImage(building.getType(), diff --git a/src/net/sf/freecol/client/gui/panel/BuildingPanel.java b/src/net/sf/freecol/client/gui/panel/BuildingPanel.java index 60b778952e..3b37a8665c 100644 --- a/src/net/sf/freecol/client/gui/panel/BuildingPanel.java +++ b/src/net/sf/freecol/client/gui/panel/BuildingPanel.java @@ -45,6 +45,7 @@ import net.sf.freecol.common.model.Ability; import net.sf.freecol.common.model.AbstractGoods; import net.sf.freecol.common.model.Building; +import net.sf.freecol.common.model.BuildingType; import net.sf.freecol.common.model.Colony; import net.sf.freecol.common.model.ProductionInfo; import net.sf.freecol.common.model.Unit; @@ -189,16 +190,16 @@ protected void update() { unitLabels.add(unitLabel); add(unitLabel); } - - ImageLibrary lib = getImageLibrary(); - Image buildingImage = lib.getScaledBuildingImage(building); - setPreferredSize(new Dimension(buildingImage.getWidth(null), - buildingImage.getHeight(null))); + + final BufferedImage image = getImageLibrary().getScaledBuildingImage(building); + setMinimumSize(new Dimension(image.getWidth(), image.getHeight())); + + final Dimension preferredSize = getImageLibrary().determineMaxSizeUsingSizeFromAllLevels(building.getType(), building.getOwner()); + setPreferredSize(preferredSize); revalidate(); repaint(); } - /** * Get the building this panel displays. * @@ -252,8 +253,13 @@ public JToolTip createToolTip() { */ @Override public void paintComponent(Graphics g) { - ImageLibrary lib = getImageLibrary(); - g.drawImage(lib.getScaledBuildingImage(building), 0, 0, this); + final ImageLibrary lib = getImageLibrary(); + final BufferedImage image = lib.getScaledBuildingImage(building); + final Dimension size = getSize(); + g.drawImage(image, + (size.width - image.getWidth()) / 2, + size.height - image.getHeight(), + this); } diff --git a/src/net/sf/freecol/client/gui/panel/BuildingsLayoutManager.java b/src/net/sf/freecol/client/gui/panel/BuildingsLayoutManager.java new file mode 100644 index 0000000000..592429d113 --- /dev/null +++ b/src/net/sf/freecol/client/gui/panel/BuildingsLayoutManager.java @@ -0,0 +1,313 @@ +/** + * Copyright (C) 2002-2023 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 . + */ + +package net.sf.freecol.client.gui.panel; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.LayoutManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import javax.swing.JViewport; + +import net.sf.freecol.client.gui.panel.ColonyPanel.BuildingsPanel; +import net.sf.freecol.client.gui.panel.ColonyPanel.BuildingsPanel.EmptyBuildingSite; + +/** + * A specific layout manager for {@code BuildingsPanel}. + * + * This layout manager tries to lay out all the buildings in a randomized + * pattern using the preferred size of the building (that is, the maximum + * required size of the building for all levels). + * + * A {@link WrapLayout} with the minimum building size is used as a + * fallback if this layout manager fails to layout all the buildings + * within the container's current size. + * + * @see BuildingsPanel + */ +public class BuildingsLayoutManager implements LayoutManager { + + /** + * A seed that is used when laying out the component. The + * same layout will be reached if no sizes (buildings or the + * container) have been changed. + */ + private long layoutSeed = 1; + + /** + * Fallback rendering if there's insufficent room for rendering + * all the buildings. + */ + private WrapLayout wrapLayout = new WrapLayout(); + + + /** + * Creates a new layout manager with a standard seed. + */ + public BuildingsLayoutManager() { + this(1); + } + + /** + * Creates a new layout manager. + * + * @param layoutSeed A seed that is used when laying out the component. The + * same layout will be reached if no sizes (buildings or the container) + * have been changed. + */ + public BuildingsLayoutManager(long layoutSeed) { + this.layoutSeed = layoutSeed; + } + + + /** + * Returns the preferred dimensions for this layout given the visible + * components in the specified target container. + * + * @param parent the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension preferredLayoutSize(Container parent) { + final Dimension size = determineInitialSize(parent); + + if (randomizedPlacement(parent, size, true)) { + // Randomized placement succeeded. + return size; + } + + // Randomized placement failed. Falling back to WrapLayout. + setAllEmptyBuildingSiteVisibility(parent, false); + return wrapLayout.layoutSize(parent, size.width, false); + } + + private Dimension determineInitialSize(Container parent) { + if (parent.getParent() != null && parent.getParent() instanceof JViewport) { + return parent.getParent().getSize(); + } + return new Dimension(0, 0); + } + + /** + * Just returns a minimum size. The initial size is determined by the + * parent of the given parent. + */ + @Override + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(1, 1); + } + + /** + * Layout of all children of the given {@code parent} + * + * @param parent The {@code Container} that should get its children + * positioned. + */ + @Override + public void layoutContainer(Container parent) { + synchronized (parent.getTreeLock()) { + Dimension size = parent.getSize(); + boolean skipRandomizedPlacement = false; + if (parent.getParent() != null && parent.getParent() instanceof JViewport) { + final Dimension newSize = parent.getParent().getSize(); + if (!size.equals(newSize)) { + skipRandomizedPlacement = true; + } + } + + if (!skipRandomizedPlacement) { + setAllEmptyBuildingSiteVisibility(parent, true); + if (randomizedPlacement(parent, size, false)) { + return; + } + } + + // Randomized placement failed. Falling back to WrapLayout. + setAllEmptyBuildingSiteVisibility(parent, false); + wrapLayout.layoutContainer(parent, false); + } + } + + private void setAllEmptyBuildingSiteVisibility(Container parent, boolean visible) { + for (Component c : parent.getComponents()) { + if (c instanceof EmptyBuildingSite) { + c.setVisible(visible); + } + } + } + + private boolean randomizedPlacement(Container parent, Dimension size, boolean dryRun) { + final Random r = new Random(layoutSeed); + + final List entries = getAllEntries(parent, size); + if (isDefinitelyNotEnoughRoomForLayout(entries, size)) { + return false; + } + sortWithLargestFirst(entries); + + final int MAX_TOTAL_TRIES = 24; + final int MAX_FREE_PLACEMENT = 6; + final int MAX_PLACE_ENTRY = 10000; + + int padding = 16; + for (int j=0; j usedRectangles = new ArrayList<>(entries.size()); + final List placeEntries; + + if (j > MAX_FREE_PLACEMENT) { + placeEntries = placeFirstFourEntriesInTheCorners(size, entries, usedRectangles); + } else { + placeEntries = entries; + } + + int entriesRemaining = placeEntries.size(); + for (Entry entry : placeEntries) { + boolean placed = false; + for (int i=0; i<(MAX_PLACE_ENTRY / (entriesRemaining * entriesRemaining)); i++) { + final int x = r.nextInt(size.width - entry.bounds.width); + final int y = r.nextInt(size.height - entry.bounds.height); + final Rectangle bounds = new Rectangle(x, y, entry.bounds.width, entry.bounds.height); + if (!overlaps(bounds, usedRectangles)) { + usedRectangles.add(new Rectangle(bounds.x - padding/2, bounds.y - padding/2, bounds.width + padding, bounds.height + padding)); + entry.bounds = bounds; + placed = true; + break; + } + } + if (!placed) { + break; + } + entriesRemaining--; + } + + if (entriesRemaining == 0) { + if (!dryRun) { + for (Entry entry : entries) { + entry.component.setBounds(entry.bounds); + } + } + + // We're done! + return true; + } + + // Reduce padding and try again. + padding /= 2; + if (padding < 0) { + padding = 0; + } + } + + return false; + } + + private List placeFirstFourEntriesInTheCorners(Dimension size, List entries, List usedRectangles) { + final Entry e0 = entries.get(0); + final Entry e1 = entries.get(1); + final Entry e2 = entries.get(2); + final Entry e3 = entries.get(3); + e0.bounds = new Rectangle(0, 0, e0.bounds.width, e0.bounds.height); + usedRectangles.add(e0.bounds); + e1.bounds = new Rectangle(0, size.height - e1.bounds.height, e1.bounds.width, e1.bounds.height); + usedRectangles.add(e1.bounds); + e2.bounds = new Rectangle(size.width - e1.bounds.width, 0, e2.bounds.width, e2.bounds.height); + usedRectangles.add(e2.bounds); + e3.bounds = new Rectangle(size.width - e3.bounds.width, size.height - e3.bounds.height, e3.bounds.width, e3.bounds.height); + usedRectangles.add(e3.bounds); + + return entries.subList(4, entries.size()); + } + + private boolean isDefinitelyNotEnoughRoomForLayout(List entries, Dimension size) { + if (entries.isEmpty()) { + return false; + } + final long minimumArea = entries.stream() + .map(BuildingsLayoutManager::areaOf) + .reduce((a, b) -> a + b).get(); + return minimumArea > ((long) size.width) * size.height; + } + + /** + * Finds all components that are children to the given parent. + * + * @return The list of children (called entries). + */ + private List getAllEntries(Container parent, Dimension size) { + final List entries = new ArrayList<>(parent.getComponentCount()); + for (Component c : parent.getComponents()) { + if (!c.isVisible()) { + continue; + } + final Dimension componentSize = c.getPreferredSize(); + entries.add(new Entry(c, new Rectangle(0, 0, componentSize.width, componentSize.height))); + } + return entries; + } + + private void sortWithLargestFirst(final List entries) { + Collections.sort(entries, (a, b) -> Long.compare(areaOf(b), areaOf(a))); + } + + private static long areaOf(Entry a) { + return ((long) a.bounds.width) * a.bounds.height; + } + + private boolean overlaps(Rectangle rectangle, List list) { + for (Rectangle r : list) { + if (rectangle.intersects(r)) { + return true; + } + } + return false; + } + + final Point determineOffsetForCentering(Dimension displaySize, Dimension contentSize) { + return new Point((displaySize.width - contentSize.width) / 2, + (displaySize.height - contentSize.height) / 2); + } + + final Rectangle reposition(Rectangle bounds, Point offset) { + return new Rectangle(bounds.x + offset.x, bounds.y + offset.y, bounds.width, bounds.height); + } + + @Override + public void addLayoutComponent(String name, Component comp) {} + + @Override + public void removeLayoutComponent(Component comp) {} + + private static class Entry { + private Component component; + private Rectangle bounds; + + public Entry(Component component, Rectangle bounds) { + this.component = component; + this.bounds = bounds; + } + } +} diff --git a/src/net/sf/freecol/client/gui/panel/CargoPanel.java b/src/net/sf/freecol/client/gui/panel/CargoPanel.java index 1048db07d7..aa18765eb6 100644 --- a/src/net/sf/freecol/client/gui/panel/CargoPanel.java +++ b/src/net/sf/freecol/client/gui/panel/CargoPanel.java @@ -19,11 +19,17 @@ package net.sf.freecol.client.gui.panel; +import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.logging.Logger; +import javax.swing.JLabel; + import net.miginfocom.swing.MigLayout; import net.sf.freecol.client.FreeColClient; import net.sf.freecol.client.gui.label.CargoLabel; @@ -47,6 +53,7 @@ public class CargoPanel extends FreeColPanel private Unit carrier; private DefaultTransferHandler defaultTransferHandler = null; + private boolean withStyling; /** * Creates this CargoPanel. @@ -54,15 +61,18 @@ public class CargoPanel extends FreeColPanel * @param freeColClient The {@code FreeColClient} for the game. * @param withTitle Should the panel have a title? */ - public CargoPanel(FreeColClient freeColClient, boolean withTitle) { + public CargoPanel(FreeColClient freeColClient, boolean withTitle, boolean withStyling) { super(freeColClient, "CargoPanelUI", - new MigLayout("wrap 6, fill, insets 0")); + new MigLayout("wrap 6, gap 0 0, insets 0", "[79!, center][79!, center][79!, center][79!, center][79!, center][79!, center]", "[100!]")); this.carrier = null; this.defaultTransferHandler = new DefaultTransferHandler(getFreeColClient(), this); + this.withStyling = withStyling; if (withTitle) { setBorder(Utility.localizedBorder("cargoOnCarrier")); + } else { + setBorder(null); } } @@ -115,6 +125,9 @@ public void update() { for (Goods g : carrier.getGoodsList()) { GoodsLabel label = new GoodsLabel(fcc, g); + label.setHorizontalTextPosition(JLabel.CENTER); + label.setVerticalTextPosition(JLabel.BOTTOM); + label.setForeground(Color.WHITE); if (isEditable()) { label.setTransferHandler(defaultTransferHandler); label.addMouseListener(dl); @@ -162,6 +175,9 @@ public void setCarrier(final Unit newCarrier) { * Update the title of this CargoPanel. */ private void updateTitle() { + if (getBorder() == null) { + return; + } Utility.localizeBorder(this, (carrier == null) ? StringTemplate.key("cargoOnCarrier") : StringTemplate.template("cargoPanel.cargoAndSpace") @@ -244,4 +260,27 @@ public void removeNotify() { defaultTransferHandler = null; } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + final Dimension size = getSize(); + final BufferedImage available = getImageLibrary().getScaledCargoHold(true); + final BufferedImage unavailable = getImageLibrary().getScaledCargoHold(false); + final int availableHolds = (carrier != null) ? carrier.getCargoCapacity() : 0; + if (withStyling) { + int x = 0; + for (int i=0; i - * Panel Layout: - *

- * |--------------------------------------------------------| - * | nameBox | netProductionPanel | - * |--------------------------------------------------------| - * | tilesPanel | buildingsPanel | - * |-------------------| | - * | populationPanel | | - * |-------------------| | - * | constructionPanel | | - * |--------------------------------------------------------| - * | inPortPanel | cargoPanel | outsideColonyPanel | - * |--------------------------------------------------------| - * | warehousePanel | - * |--------------------------------------------------------| */ public final class ColonyPanel extends PortPanel implements PropertyChangeListener { private static final Logger logger = Logger.getLogger(ColonyPanel.class.getName()); - /** The height of the area in which autoscrolling should happen. */ + /** The height of the area in whichcomponentRe autoscrolling should happen. */ public static final int SCROLL_AREA_HEIGHT = 40; /** The speed of the scrolling. */ @@ -142,14 +152,14 @@ public final class ColonyPanel extends PortPanel private JButton fillButton = Utility.localizedButton("load"); - private JButton warehouseButton - = Utility.localizedButton("colonyPanel.warehouse"); + private JButton warehouseButton = new FreeColButton(Messages.message("colonyPanel.warehouse")) + .withButtonStyle(ButtonStyle.SIMPLE); - private JButton buildQueueButton - = Utility.localizedButton("colonyPanel.buildQueue"); + private JButton buildQueueButton = new FreeColButton(Messages.message("colonyPanel.buildQueue")) + .withButtonStyle(ButtonStyle.SIMPLE); - private JButton colonyUnitsButton - = Utility.localizedButton("colonyPanel.colonyUnits"); + private JButton colonyUnitsButton = new FreeColButton(Messages.message("colonyPanel.colonyUnits")) + .withButtonStyle(ButtonStyle.SIMPLE); // Only present in debug mode private JButton setGoodsButton = null; @@ -258,11 +268,9 @@ public final class ColonyPanel extends PortPanel public ColonyPanel(FreeColClient freeColClient, Colony colony) { super(freeColClient, new MigLayout()); - getMigLayout().setLayoutConstraints("fill, wrap 2, insets 2"); - getMigLayout().setColumnConstraints("[" + getTilesPanelGuiScaledDimension().width + "px!][fill]"); - getMigLayout().setRowConstraints("[growprio 100,shrinkprio 10][]0[]0[]" - + "[growprio 150,shrinkprio 50]" - + "[][]"); + getMigLayout().setLayoutConstraints("fill, wrap 2, insets 0, gap 0 0"); + getMigLayout().setColumnConstraints("[fill][474!]"); + getMigLayout().setRowConstraints("[]0[]0[][growprio 150,shrinkprio 50][][]"); final Player player = getMyPlayer(); // Do not just use colony.getOwner() == getMyPlayer() because @@ -271,8 +279,7 @@ public ColonyPanel(FreeColClient freeColClient, Colony colony) { editable = colony.getOwner().getId().equals(player.getId()); // Only enable the set goods button in debug mode when not spying - if (editable - && FreeColDebugger.isInDebugMode(FreeColDebugger.DebugMode.MENUS)) { + if (editable && FreeColDebugger.isInDebugMode(FreeColDebugger.DebugMode.MENUS)) { setGoodsButton = Utility.localizedButton("colonyPanel.setGoods"); traceWorkButton = Utility.localizedButton("colonyPanel.traceWork"); } @@ -359,33 +366,68 @@ public ColonyPanel(FreeColClient freeColClient, Colony colony) { netProductionPanel.setOpaque(false); buildingsPanel = new BuildingsPanel(); + buildingsPanel.setOpaque(false); buildingsScroll = new JScrollPane(buildingsPanel, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER) { + + @Override + public Dimension getPreferredSize() { + return new Dimension(1, 1); + } + }; + + /* + * Dirty workaround for scrollbar flickering and missing layouts. In the future, we + * should instead use the JScrollBar directly from the buildingsPanel (and integrate + * with the layout manager) rather than relying on JScrollPane. + */ + buildingsScroll.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + final Dimension preferredSize = buildingsPanel.getLayout().preferredLayoutSize(buildingsPanel); + final Dimension viewportSize = buildingsScroll.getViewport().getSize(); + if (!preferredSize.equals(buildingsPanel.getSize())) { + buildingsPanel.setSize(preferredSize); + buildingsScroll.validate(); + } + if (preferredSize.width <= viewportSize.width + && preferredSize.height <= viewportSize.height) { + buildingsScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + } else { + buildingsScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + } + } + }); + buildingsScroll.getVerticalScrollBar().setUnitIncrement(16); buildingsScroll.getViewport().setOpaque(false); - buildingsPanel.setOpaque(false); - buildingsScroll.setBorder(Utility.ETCHED_BORDER); + buildingsScroll.setBorder(null); cargoPanel = new ColonyCargoPanel(freeColClient); cargoScroll = new JScrollPane(cargoPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - cargoScroll.setBorder(Utility.ETCHED_BORDER); + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + cargoScroll.setBorder(null); constructionPanel = new ConstructionPanel(freeColClient, colony, true); inPortPanel = new ColonyInPortPanel(); - inPortScroll = new JScrollPane(inPortPanel); + inPortScroll = new JScrollPane(inPortPanel, + ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); inPortScroll.getVerticalScrollBar().setUnitIncrement(16); - inPortScroll.setBorder(Utility.ETCHED_BORDER); + inPortScroll.setBorder(null); + inPortScroll.setOpaque(false); + inPortScroll.getViewport().setOpaque(false); outsideColonyPanel = new OutsideColonyPanel(); outsideColonyScroll = new JScrollPane(outsideColonyPanel, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); outsideColonyScroll.getVerticalScrollBar().setUnitIncrement(16); - outsideColonyScroll.setBorder(Utility.ETCHED_BORDER); + outsideColonyScroll.setBorder(null); + outsideColonyScroll.setOpaque(false); + outsideColonyScroll.getViewport().setOpaque(false); populationPanel = new PopulationPanel(); @@ -395,7 +437,7 @@ public ColonyPanel(FreeColClient freeColClient, Colony colony) { warehouseScroll = new JScrollPane(warehousePanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - warehouseScroll.setBorder(Utility.ETCHED_BORDER); + warehouseScroll.setBorder(FreeColImageBorder.colonyWarehouseBorder); InputMap nameIM = new ComponentInputMap(this.nameBox); nameIM.put(KeyStroke.getKeyStroke("LEFT"), "selectPrevious2"); @@ -403,16 +445,18 @@ public ColonyPanel(FreeColClient freeColClient, Colony colony) { SwingUtilities.replaceUIInputMap(this.nameBox, JComponent.WHEN_IN_FOCUSED_WINDOW, nameIM); - if (getGUI().getMapViewDimension().height < 770) { + if (getGUI().getMapViewDimension().height < 700) { /* - * Reduces the border size so that the Colony panel - * can get slightly bigger. + * TODO: Remove all borders and show the panel covering the + * entire screen (except for the menu bar. */ - setBorder(FreeColImageBorder.panelWithoutShadowBorder); } - + + setBorder(FreeColImageBorder.innerColonyPanelBorder); initialize(colony); - getGUI().restoreSavedSize(this, new Dimension(1050, 675)); + + final Dimension startingSize = freeColClient.getGUI().getFixedImageLibrary().scale(new Dimension(1280, 700)); + getGUI().restoreSavedSize(this, startingSize); setEscapeAction(new AbstractAction() { @Override @@ -421,8 +465,108 @@ public void actionPerformed(ActionEvent ae) { } }); } + + /** + * Paints parts of the background/skins for the ColonyPanel. In the future + * we will probably want to generalize this approach in order to allow + * mods to define complex skins. + */ + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + /* + * Painting the backgrounds here avoids the need for a JLayeredPane (since we + * want different backgrounds to overlay with partial transparency). + */ + + final Dimension size = getSize(); + + final BufferedImage colonyDocks = getImageLibrary().getColonyDocks(); + if (colonyDocks != null) { + final int docksBottomLeftX = inPortScroll.getX(); + final int docksBottomLeftY = inPortScroll.getY() + inPortScroll.getHeight(); + int y = docksBottomLeftY - colonyDocks.getHeight(); + g.drawImage(colonyDocks, docksBottomLeftX, y, null); + + final BufferedImage colonyDocksSky = getImageLibrary().getColonyDocksSky(); + if (colonyDocksSky != null) { + while (y > 0) { + y -= colonyDocksSky.getHeight(); + g.drawImage(colonyDocksSky, docksBottomLeftX, y, null); + } + } + + } + + final BufferedImage unavailable = getImageLibrary().getScaledCargoHold(false); + if (unavailable != null) { + final int cargoHoldTopRightX = cargoScroll.getX() + cargoScroll.getWidth(); + final int cargoHoldTopRightY = cargoScroll.getY(); + g.drawImage(unavailable, cargoHoldTopRightX, cargoHoldTopRightY, null); + } + + final BufferedImage upperRightBackground = getImageLibrary().getColonyUpperRightBackground(); + if (upperRightBackground != null) { + final Insets insets = getInsets(); + g.drawImage(upperRightBackground, size.width - upperRightBackground.getWidth() - insets.right, insets.top, null); + } + + final BufferedImage nwBorder = getImageLibrary().getScaledImage("image.border.wooden.nw"); + final BufferedImage wBorder = getImageLibrary().getScaledImage("image.border.wooden.w"); + if (nwBorder != null && wBorder != null) { + final int x = okButton.getX() + okButton.getWidth(); + int y = okButton.getY(); + g.drawImage(nwBorder, + x, + y, + null); + y += nwBorder.getHeight(); + + while (y < size.height) { + g.drawImage(wBorder, + x, + y, + null); + y += wBorder.getHeight(); + } + } + + final BufferedImage sBorder = getImageLibrary().getScaledImage("image.border.wooden.s"); + if (sBorder != null) { + int x = 0; + int y = warehouseScroll.getY() + warehouseScroll.getHeight(); + while (x < size.width) { + g.drawImage(sBorder, x, y, null); + x += sBorder.getWidth(); + } + } + + if (nwBorder != null && wBorder != null) { + int x = warehouseScroll.getX() - nwBorder.getWidth(); + int y = warehouseScroll.getY(); + g.drawImage(nwBorder, x, y, null); + y += nwBorder.getHeight(); + + while (y < size.height) { + g.drawImage(wBorder, + x, + y, + null); + y += wBorder.getHeight(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public Border getFrameBorder() { + return FreeColImageBorder.outerColonyPanelBorder; + } - + /** * Set the current colony. * @@ -436,7 +580,7 @@ private Dimension getTilesPanelGuiScaledDimension() { final int tilesPanelWidth = 3 * getImageLibrary().getTileSize().width; final int tilesPanelHeight = 3 * getImageLibrary().getTileSize().height; final Dimension size = getImageLibrary().getTileSize(); - final int extraHeight = size.height / 2; + final int extraHeight = tilesPanelTopOffset(size); return new Dimension(tilesPanelWidth, tilesPanelHeight + extraHeight); } @@ -503,29 +647,51 @@ private void initialize(Colony colony) { warehousePanel.initialize(); final Dimension tilesScrollDimension = getTilesPanelGuiScaledDimension(); + add(buildingsScroll, "span 1 4, grow"); + + // TODO: Display the name: + //add(this.nameBox, "grow, span"); + + JPanel upperRightPanel = new JPanel(new MigLayout("wrap 1", "[center]")); + upperRightPanel.setOpaque(false); + upperRightPanel.add(tilesPanel, "width " + tilesScrollDimension.width + "px!, height " + tilesScrollDimension.height +"px!, top, center"); + + final FreeColImageBorder border = FreeColImageBorder.innerColonyPanelBorder; + final Insets borderInsets = border.getBorderInsets(this); + upperRightPanel.add(netProductionPanel, "grow"); - add(this.nameBox, "grow"); - add(netProductionPanel, "grow"); - add(tilesPanel, "width " + tilesScrollDimension.width + "px!, height " + tilesScrollDimension.height +"px!, top"); - add(buildingsScroll, "span 1 3, grow"); - add(populationPanel, "grow"); - add(constructionPanel, "grow, top"); - add(inPortScroll, "span, split 3, grow, sg, height 60:160:"); - add(cargoScroll, "grow, sg, height 60:160:"); - add(outsideColonyScroll, "grow, sg, height 60:160:"); - add(warehouseScroll, "span, height " + (ImageLibrary.ICON_SIZE.height * 2) + "!, growx"); - int buttonFields = 6; - if (setGoodsButton != null) buttonFields++; - if (traceWorkButton != null) buttonFields++; - add(unloadButton, "span, split " + Integer.toString(buttonFields) - + ", align center"); - add(fillButton); - add(warehouseButton); - add(buildQueueButton); - add(colonyUnitsButton); - if (setGoodsButton != null) add(setGoodsButton); - if (traceWorkButton != null) add(traceWorkButton); - add(okButton, "gapbefore push"); // tag ok + // TODO: Display the numbers in new components: + //add(populationPanel, "grow"); + + final JPanel buttonPanel = new JPanel(new MigLayout("fill, wrap 1, gap 0 0, ins 0", "", "")); + buttonPanel.setOpaque(false); + + /* + * TODO: Don't think we need these, but they can be added to the + * in-port panel if we do. + */ + //buttonPanel.add(unloadButton); + //buttonPanel.add(fillButton); + + buttonPanel.add(buildQueueButton, "sg"); + buttonPanel.add(warehouseButton, "sg"); + buttonPanel.add(colonyUnitsButton, "sg"); + + upperRightPanel.add(buttonPanel, "sgy, split 2, left"); + upperRightPanel.add(constructionPanel, "sgy, grow, top"); + add(upperRightPanel, "span 1 3, grow, gapbefore 53, width 400!"); + + add(inPortScroll, "span, grow, height 96!, gaptop push"); + add(outsideColonyScroll, "grow, height 96!"); + add(cargoScroll, "grow, height 100!"); + add(warehouseScroll, "span, split, height 48!, growx"); + if (setGoodsButton != null) { + add(setGoodsButton); + } + if (traceWorkButton != null) { + add(traceWorkButton); + } + add(okButton, "gapbefore push, height 48!"); // tag ok update(); } @@ -1133,7 +1299,7 @@ public final class ColonyCargoPanel extends CargoPanel { * @param freeColClient The {@code FreeColClient} for the game. */ public ColonyCargoPanel(FreeColClient freeColClient) { - super(freeColClient, true); + super(freeColClient, false, true); } @@ -1296,18 +1462,23 @@ public void removeNotify() { * A panel that holds UnitLabels that represent Units that are standing in * front of a colony. */ - public final class OutsideColonyPanel extends UnitPanel - implements DropTarget { + public final class OutsideColonyPanel extends UnitPanel implements DropTarget { /** * Create this OutsideColonyPanel. */ public OutsideColonyPanel() { super("OutsideColonyPanelUI", - new MigLayout("wrap 3, fill, insets 0"), ColonyPanel.this, + new MigLayout("ins 0, gapy 0, align center bottom", "", "[bottom]"), + ColonyPanel.this, null, ColonyPanel.this.isEditable()); - setBorder(Utility.localizedBorder("colonyPanel.outsideColony")); + /* + * TODO: Decide if we need to have a title for "Outside the colony". If so, + * make it nicer than this one. + */ + //setBorder(Utility.localizedBorder("colonyPanel.outsideColony")); + setOpaque(false); } @@ -1447,10 +1618,15 @@ public final class ColonyInPortPanel extends InPortPanel { * Creates this ColonyInPortPanel. */ public ColonyInPortPanel() { - super(new MigLayout("wrap 3, fill, insets 0"), ColonyPanel.this, + super(new MigLayout("ins 0, gapy 0, align center bottom", "", "[bottom]"), ColonyPanel.this, null, ColonyPanel.this.isEditable()); - setBorder(Utility.localizedBorder("colonyPanel.inPort")); + /* + * TODO: Decide if we need to have a title for "In port". If so, + * make it nicer than this one. + */ + //setTitleInsidePanel(Messages.message("colonyPanel.inPort")); + setOpaque(false); } @@ -1582,8 +1758,8 @@ public void update() { int count = colony.getGoodsCount(goodsType); if (count >= threshold) { Goods goods = new Goods(game, colony, goodsType, count); - GoodsLabel goodsLabel - = new GoodsLabel(getFreeColClient(), goods); + GoodsLabel goodsLabel = new GoodsLabel(getFreeColClient(), goods); + goodsLabel.setForeground(Color.WHITE); if (ColonyPanel.this.isEditable()) { goodsLabel.setTransferHandler(defaultTransferHandler); goodsLabel.addMouseListener(pressListener); @@ -1680,6 +1856,14 @@ public void removeNotify() { removeAll(); setLayout(null); } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + final BufferedImage colonyWarehouseBackground = getImageLibrary().getColonyWarehouseBackground(); + ImageUtils.drawTiledImage(colonyWarehouseBackground, g, this, getInsets()); + } } @@ -1702,7 +1886,8 @@ public void mousePressed(MouseEvent e) { * Creates this BuildingsPanel. */ public BuildingsPanel() { - super("BuildingsPanelUI", new MigLayout("fill, wrap 4, insets 0, gap 0:10:10:push 0:10:10:push")); + // TODO: Use different seeds per colony for BuildingsLayoutManager? + super("BuildingsPanelUI", new BuildingsLayoutManager()); } @@ -1713,11 +1898,37 @@ public void initialize() { final Colony colony = getColony(); if (colony == null) return; cleanup(); - - for (Building building : sort(colony.getBuildings())) { - ASingleBuildingPanel aSBP = new ASingleBuildingPanel(building); - aSBP.initialize(); - add(aSBP); + + final List allBuildableTypes = getSpecification().getBuildingTypeList().stream() + .filter(bt -> bt.getUpgradesFrom() == null) + .collect(Collectors.toList()); + final Random random = new Random(13 * getColony().getTile().getX() + 23 * getColony().getTile().getY()); + Collections.shuffle(allBuildableTypes, random); + + Map> constructedBuildings = colony.getBuildings().stream().collect(Collectors.groupingBy(g -> g.getType().getFirstLevel())); + for (BuildingType bt : allBuildableTypes) { + if (bt.isDefenceType()) { + continue; + } + if (bt.hasAbility(Ability.PRODUCE_IN_WATER)) { + continue; + } + final List btBuildings = constructedBuildings.get(bt); + if (btBuildings == null) { + final JPanel emptyPlot = new EmptyBuildingSite(); + final Dimension size = getImageLibrary().determineMaxSizeUsingSizeFromAllLevels(bt, colony.getOwner()); + emptyPlot.setMinimumSize(size); + emptyPlot.setPreferredSize(size); + emptyPlot.setSize(size); + add(emptyPlot); + } else { + if (btBuildings.size() != 1) { + throw new IllegalStateException("Expected at most one building of each type."); + } + ASingleBuildingPanel aSBP = new ASingleBuildingPanel(btBuildings.get(0)); + aSBP.initialize(); + add(aSBP); + } } update(); @@ -1906,6 +2117,34 @@ public void propertyChange(PropertyChangeEvent event) { ColonyPanel.this.updateProduction(); } } + + public final class EmptyBuildingSite extends JPanel{ + EmptyBuildingSite() { + setOpaque(false); + } + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + final ImageLibrary lib = getImageLibrary(); + final BufferedImage image = lib.getScaledBuildingEmptyLandImage(); + final Dimension size = getSize(); + g.drawImage(image, + (size.width - image.getWidth()) / 2, + size.height - image.getHeight(), + this); + } + } + } + + private int tilesPanelTopOffset(final Dimension tileSize) { + /* + * TODO: tileSize.height / 2 is needed to avoid clipping mountains, + * but that takes a lot of space. Perhaps make the tile render + * above the border (and clip when the panel is displayed + * without them)? + */ + final int topOffset = tileSize.height / 4; + return topOffset; } /** @@ -2032,19 +2271,13 @@ public void paintComponent(Graphics g) { final Colony colony = getColony(); if (colony != null) { final Dimension size = getImageLibrary().getTileSize(); - final int topOffset = topOffset(size); + final int topOffset = tilesPanelTopOffset(size); g.translate(0, topOffset); getGUI().displayColonyTiles((Graphics2D)g, tiles, colony); g.translate(0, -topOffset); } } - - private int topOffset(final Dimension tileSize) { - final int topOffset = tileSize.height / 4; - return topOffset; - } - /** * Panel for visualizing a {@code ColonyTile}. The * component itself is not visible, however the content of the @@ -2071,7 +2304,7 @@ public ASingleTilePanel(ColonyTile colonyTile, int x, int y) { setOpaque(false); // Size and position: Dimension size = getImageLibrary().getTileSize(); - final int topOffset = topOffset(size); + final int topOffset = tilesPanelTopOffset(size); setSize(size); setLocation(((2 - x) + y) * size.width / 2, (x + y) * size.height / 2 + topOffset); diff --git a/src/net/sf/freecol/client/gui/panel/EuropePanel.java b/src/net/sf/freecol/client/gui/panel/EuropePanel.java index 979908ae81..3e87d48ecf 100644 --- a/src/net/sf/freecol/client/gui/panel/EuropePanel.java +++ b/src/net/sf/freecol/client/gui/panel/EuropePanel.java @@ -649,7 +649,7 @@ public EuropePanel(FreeColClient freeColClient, boolean header) { toAmericaPanel = new DestinationPanel(); toEuropePanel = new DestinationPanel(); inPortPanel = new EuropeInPortPanel(); - cargoPanel = new CargoPanel(freeColClient, true); + cargoPanel = new CargoPanel(freeColClient, true, false); europeanDocksPanel = new EuropeanDocksPanel(); marketPanel = new MarketPanel(this); log = new TransactionLog(); diff --git a/src/net/sf/freecol/client/gui/panel/FreeColImageBorder.java b/src/net/sf/freecol/client/gui/panel/FreeColImageBorder.java index af8499c90f..07f19b2440 100644 --- a/src/net/sf/freecol/client/gui/panel/FreeColImageBorder.java +++ b/src/net/sf/freecol/client/gui/panel/FreeColImageBorder.java @@ -49,7 +49,10 @@ public class FreeColImageBorder extends AbstractBorder { 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 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;