Skip to content

Commit

Permalink
tab Players - tooltips for technologies (#12853)
Browse files Browse the repository at this point in the history
* Fix issue 12826 (UnitSeparator#categorize:213 - java.util.ConcurrentModificationException)

PlacePanel.java
- new method updateUnitsInUnitsToPlacePanel that ensures copying the unit collection (existed already in method updateStep, but not in gameDataChanged or updateUnits)
- Cleanup: extract new methods from declaration of variable placeMapSelectionListener which are getUnitsToPlace, getScrollPaneFromChooser, getPreferredHeight and getPreferredWidth

* PlayerUnitsPanel ToDo done via redraw

Replace
invalidate(); validate(); revalidate();getParent().invalidate();
with SwingComponents.redraw(this);

* tab Players - tooltips for technologies

Smaller fixes:
- SwingConstants instead of JLable
- method setStatColumns with List.toArray
- rename gameData to gameDataSync
- introduce synchronized method TechTableModel.getDataAndInitRowMap()
  • Loading branch information
frigoref authored Aug 23, 2024
1 parent 936b1df commit be37ef5
Showing 1 changed file with 55 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
Expand All @@ -42,6 +41,7 @@
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import org.jetbrains.annotations.NotNull;

class StatPanel extends JPanel implements GameDataChangeListener {
private static final long serialVersionUID = 4340684166664492498L;
Expand All @@ -66,7 +66,7 @@ class StatPanel extends JPanel implements GameDataChangeListener {
protected void initLayout() {
final boolean hasTech =
!TechAdvance.getTechAdvances(gameData.getTechnologyFrontier(), null).isEmpty();
// do no include a grid box for tech if there is no tech
// do not include a grid box for tech if there is no tech
setLayout(new GridLayout((hasTech ? 2 : 1), 1));
add(new JScrollPane(createPlayersTable()));
// if no technologies, do not show the tech table
Expand All @@ -80,9 +80,9 @@ private JTable createPlayersTable() {
statsTable.getTableHeader().setReorderingAllowed(false);
// By default, right-align columns and their headers.
((DefaultTableCellRenderer) statsTable.getTableHeader().getDefaultRenderer())
.setHorizontalAlignment(JLabel.RIGHT);
.setHorizontalAlignment(SwingConstants.RIGHT);
((DefaultTableCellRenderer) statsTable.getDefaultRenderer(String.class))
.setHorizontalAlignment(JLabel.RIGHT);
.setHorizontalAlignment(SwingConstants.RIGHT);
final TableColumn leftColumn = statsTable.getColumnModel().getColumn(0);
leftColumn.setPreferredWidth(175);
// The left column should be left-aligned. Override the renderers for it to defaults.
Expand All @@ -107,9 +107,22 @@ private JTable createTechTable() {
value.setToolTipText(player);
column.setHeaderValue(value);
}
// show tooltip for technology names
final TableCellRenderer techNameComponentRenderer = new TechNameComponentRenderer();
final TableColumn techNameColumn = techTable.getColumnModel().getColumn(0);
techNameColumn.setHeaderRenderer(techNameComponentRenderer);
techNameColumn.setCellRenderer(techNameComponentRenderer);
return techTable;
}

static final class TechNameComponentRenderer extends DefaultTableCellRenderer {
@Override
public void setValue(Object aValue) {
setToolTipText(aValue.toString());
super.setValue(aValue);
}
}

public void setGameData(final GameData data) {
gameData.removeDataChangeListener(this);
gameData = data;
Expand Down Expand Up @@ -179,40 +192,38 @@ class StatTableModel extends AbstractTableModel {
}

void setStatColumns() {
stats = new IStat[] {new PuStat(), new ProductionStat(), new UnitsStat(), new TuvStat()};
final List<IStat> statsList =
Arrays.asList(new PuStat(), new ProductionStat(), new UnitsStat(), new TuvStat());
if (gameData.getMap().getTerritories().stream().anyMatch(Matches.territoryIsVictoryCity())) {
final List<IStat> stats = new ArrayList<>(List.of(StatPanel.this.stats));
stats.add(new VictoryCityStat());
StatPanel.this.stats = stats.toArray(new IStat[0]);
statsList.add(new VictoryCityStat());
}
// only add the vps in pacific
if (Properties.getPacificTheater(gameData.getProperties())) {
final List<IStat> stats = new ArrayList<>(List.of(StatPanel.this.stats));
stats.add(new VpStat());
StatPanel.this.stats = stats.toArray(new IStat[0]);
statsList.add(new VpStat());
}
StatPanel.this.stats = statsList.toArray(new IStat[0]);
}

private synchronized void loadData() {
// copy so that the object doesn't change underneath us
final GameData gameData = StatPanel.this.gameData;
try (GameData.Unlocker ignored = gameData.acquireReadLock()) {
final List<GamePlayer> players = gameData.getPlayerList().getSortedPlayers();
final List<String> alliances = getAlliancesToShow(gameData.getAllianceTracker());
final GameData gameDataSync = StatPanel.this.gameData;
try (GameData.Unlocker ignored = gameDataSync.acquireReadLock()) {
final List<GamePlayer> players = gameDataSync.getPlayerList().getSortedPlayers();
final List<String> alliances = getAlliancesToShow(gameDataSync.getAllianceTracker());
collectedData = new String[players.size() + alliances.size()][stats.length + 1];
int row = 0;
for (final GamePlayer player : players) {
collectedData[row][0] = player.getName();
for (int i = 0; i < stats.length; i++) {
double value = stats[i].getValue(player, gameData, uiContext.getMapData());
double value = stats[i].getValue(player, gameDataSync, uiContext.getMapData());
collectedData[row][i + 1] = IStat.DECIMAL_FORMAT.format(value);
}
row++;
}
for (final String alliance : alliances) {
collectedData[row][0] = "<html><b>" + alliance;
for (int i = 0; i < stats.length; i++) {
double value = stats[i].getValue(alliance, gameData, uiContext.getMapData());
double value = stats[i].getValue(alliance, gameDataSync, uiContext.getMapData());
collectedData[row][i + 1] = IStat.DECIMAL_FORMAT.format(value);
}
row++;
Expand All @@ -228,7 +239,7 @@ private List<String> getAlliancesToShow(AllianceTracker tracker) {
}

/*
* Re-calcs the underlying data in a lazy manner.
* Re-calculations the underlying data in a lazy manner.
* Limitation: This is not a thread-safe implementation.
*/
@Override
Expand Down Expand Up @@ -287,33 +298,40 @@ class TechTableModel extends AbstractTableModel {
for (int i = 0; i < colList.length; i++) {
colMap.put(colList[i], i + 1);
}
data = getDataAndInitRowMap();
clearAdvances();
}

private synchronized String[] @NotNull [] getDataAndInitRowMap() {
final String[][] dataTable;
boolean useTech = false;
// copy so that the object doesn't change underneath us
final GameData gameData = StatPanel.this.gameData;
try (GameData.Unlocker ignored = gameData.acquireReadLock()) {
final int numTechs = TechAdvance.getTechAdvances(gameData.getTechnologyFrontier()).size();
if (gameData.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
final GameData gameDataSync = StatPanel.this.gameData;
try (GameData.Unlocker ignored = gameDataSync.acquireReadLock()) {
final int numTechs =
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier()).size();
if (gameDataSync.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
useTech = true;
data = new String[numTechs + 1][colList.length + 2];
dataTable = new String[numTechs + 1][colList.length + 2];
} else {
data = new String[numTechs][colList.length + 1];
dataTable = new String[numTechs][colList.length + 1];
}
}
/* Load the technology -> row mapping */
int row = 0;
if (useTech) {
rowMap.put("Tokens", row);
data[row][0] = "Tokens";
dataTable[row][0] = "Tokens";
row++;
}
final List<TechAdvance> techAdvances =
TechAdvance.getTechAdvances(gameData.getTechnologyFrontier(), null);
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier(), null);
for (final TechAdvance tech : techAdvances) {
rowMap.put(tech.getName(), row);
data[row][0] = tech.getName();
dataTable[row][0] = tech.getName();
row++;
}
clearAdvances();
return dataTable;
}

private void clearAdvances() {
Expand All @@ -334,34 +352,34 @@ private void initColList() {
Arrays.sort(colList);
}

void update() {
synchronized void update() {
clearAdvances();
// copy so that the object doesn't change underneath us
final GameData gameData = StatPanel.this.gameData;
try (GameData.Unlocker ignored = gameData.acquireReadLock()) {
for (final GamePlayer pid : gameData.getPlayerList().getPlayers()) {
final GameData gameDataSync = StatPanel.this.gameData;
try (GameData.Unlocker ignored = gameDataSync.acquireReadLock()) {
for (final GamePlayer pid : gameDataSync.getPlayerList().getPlayers()) {
if (colMap.get(pid.getName()) == null) {
throw new IllegalStateException(
"Unexpected player in GameData.getPlayerList()" + pid.getName());
}
final int col = colMap.get(pid.getName());
int row = 0;
if (gameData.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
if (gameDataSync.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
final int tokens = pid.getResources().getQuantity(Constants.TECH_TOKENS);
data[row][col] = Integer.toString(tokens);
}
final List<TechAdvance> advancesAll =
TechAdvance.getTechAdvances(gameData.getTechnologyFrontier());
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier());
final List<TechAdvance> has =
TechAdvance.getTechAdvances(gameData.getTechnologyFrontier(), pid);
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier(), pid);
for (final TechAdvance advance : advancesAll) {
if (!has.contains(advance)) {
row = rowMap.get(advance.getName());
data[row][col] = "-";
}
}
for (final TechAdvance advance :
TechTracker.getCurrentTechAdvances(pid, gameData.getTechnologyFrontier())) {
TechTracker.getCurrentTechAdvances(pid, gameDataSync.getTechnologyFrontier())) {
row = rowMap.get(advance.getName());
data[row][col] = "X";
}
Expand All @@ -378,7 +396,7 @@ public String getColumnName(final int col) {
}

/*
* Recalcs the underlying data in a lazy manner.
* Recalculations the underlying data in a lazy manner.
* Limitation: This is not a thread-safe implementation.
*/
@Override
Expand Down

0 comments on commit be37ef5

Please sign in to comment.