Skip to content

Commit be37ef5

Browse files
authored
tab Players - tooltips for technologies (#12853)
* 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()
1 parent 936b1df commit be37ef5

File tree

1 file changed

+55
-37
lines changed
  • game-app/game-core/src/main/java/games/strategy/triplea/ui

1 file changed

+55
-37
lines changed

game-app/game-core/src/main/java/games/strategy/triplea/ui/StatPanel.java

+55-37
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.awt.Component;
2323
import java.awt.GridLayout;
2424
import java.awt.Image;
25-
import java.util.ArrayList;
2625
import java.util.Arrays;
2726
import java.util.HashMap;
2827
import java.util.List;
@@ -42,6 +41,7 @@
4241
import javax.swing.table.JTableHeader;
4342
import javax.swing.table.TableCellRenderer;
4443
import javax.swing.table.TableColumn;
44+
import org.jetbrains.annotations.NotNull;
4545

4646
class StatPanel extends JPanel implements GameDataChangeListener {
4747
private static final long serialVersionUID = 4340684166664492498L;
@@ -66,7 +66,7 @@ class StatPanel extends JPanel implements GameDataChangeListener {
6666
protected void initLayout() {
6767
final boolean hasTech =
6868
!TechAdvance.getTechAdvances(gameData.getTechnologyFrontier(), null).isEmpty();
69-
// do no include a grid box for tech if there is no tech
69+
// do not include a grid box for tech if there is no tech
7070
setLayout(new GridLayout((hasTech ? 2 : 1), 1));
7171
add(new JScrollPane(createPlayersTable()));
7272
// if no technologies, do not show the tech table
@@ -80,9 +80,9 @@ private JTable createPlayersTable() {
8080
statsTable.getTableHeader().setReorderingAllowed(false);
8181
// By default, right-align columns and their headers.
8282
((DefaultTableCellRenderer) statsTable.getTableHeader().getDefaultRenderer())
83-
.setHorizontalAlignment(JLabel.RIGHT);
83+
.setHorizontalAlignment(SwingConstants.RIGHT);
8484
((DefaultTableCellRenderer) statsTable.getDefaultRenderer(String.class))
85-
.setHorizontalAlignment(JLabel.RIGHT);
85+
.setHorizontalAlignment(SwingConstants.RIGHT);
8686
final TableColumn leftColumn = statsTable.getColumnModel().getColumn(0);
8787
leftColumn.setPreferredWidth(175);
8888
// The left column should be left-aligned. Override the renderers for it to defaults.
@@ -107,9 +107,22 @@ private JTable createTechTable() {
107107
value.setToolTipText(player);
108108
column.setHeaderValue(value);
109109
}
110+
// show tooltip for technology names
111+
final TableCellRenderer techNameComponentRenderer = new TechNameComponentRenderer();
112+
final TableColumn techNameColumn = techTable.getColumnModel().getColumn(0);
113+
techNameColumn.setHeaderRenderer(techNameComponentRenderer);
114+
techNameColumn.setCellRenderer(techNameComponentRenderer);
110115
return techTable;
111116
}
112117

118+
static final class TechNameComponentRenderer extends DefaultTableCellRenderer {
119+
@Override
120+
public void setValue(Object aValue) {
121+
setToolTipText(aValue.toString());
122+
super.setValue(aValue);
123+
}
124+
}
125+
113126
public void setGameData(final GameData data) {
114127
gameData.removeDataChangeListener(this);
115128
gameData = data;
@@ -179,40 +192,38 @@ class StatTableModel extends AbstractTableModel {
179192
}
180193

181194
void setStatColumns() {
182-
stats = new IStat[] {new PuStat(), new ProductionStat(), new UnitsStat(), new TuvStat()};
195+
final List<IStat> statsList =
196+
Arrays.asList(new PuStat(), new ProductionStat(), new UnitsStat(), new TuvStat());
183197
if (gameData.getMap().getTerritories().stream().anyMatch(Matches.territoryIsVictoryCity())) {
184-
final List<IStat> stats = new ArrayList<>(List.of(StatPanel.this.stats));
185-
stats.add(new VictoryCityStat());
186-
StatPanel.this.stats = stats.toArray(new IStat[0]);
198+
statsList.add(new VictoryCityStat());
187199
}
188200
// only add the vps in pacific
189201
if (Properties.getPacificTheater(gameData.getProperties())) {
190-
final List<IStat> stats = new ArrayList<>(List.of(StatPanel.this.stats));
191-
stats.add(new VpStat());
192-
StatPanel.this.stats = stats.toArray(new IStat[0]);
202+
statsList.add(new VpStat());
193203
}
204+
StatPanel.this.stats = statsList.toArray(new IStat[0]);
194205
}
195206

196207
private synchronized void loadData() {
197208
// copy so that the object doesn't change underneath us
198-
final GameData gameData = StatPanel.this.gameData;
199-
try (GameData.Unlocker ignored = gameData.acquireReadLock()) {
200-
final List<GamePlayer> players = gameData.getPlayerList().getSortedPlayers();
201-
final List<String> alliances = getAlliancesToShow(gameData.getAllianceTracker());
209+
final GameData gameDataSync = StatPanel.this.gameData;
210+
try (GameData.Unlocker ignored = gameDataSync.acquireReadLock()) {
211+
final List<GamePlayer> players = gameDataSync.getPlayerList().getSortedPlayers();
212+
final List<String> alliances = getAlliancesToShow(gameDataSync.getAllianceTracker());
202213
collectedData = new String[players.size() + alliances.size()][stats.length + 1];
203214
int row = 0;
204215
for (final GamePlayer player : players) {
205216
collectedData[row][0] = player.getName();
206217
for (int i = 0; i < stats.length; i++) {
207-
double value = stats[i].getValue(player, gameData, uiContext.getMapData());
218+
double value = stats[i].getValue(player, gameDataSync, uiContext.getMapData());
208219
collectedData[row][i + 1] = IStat.DECIMAL_FORMAT.format(value);
209220
}
210221
row++;
211222
}
212223
for (final String alliance : alliances) {
213224
collectedData[row][0] = "<html><b>" + alliance;
214225
for (int i = 0; i < stats.length; i++) {
215-
double value = stats[i].getValue(alliance, gameData, uiContext.getMapData());
226+
double value = stats[i].getValue(alliance, gameDataSync, uiContext.getMapData());
216227
collectedData[row][i + 1] = IStat.DECIMAL_FORMAT.format(value);
217228
}
218229
row++;
@@ -228,7 +239,7 @@ private List<String> getAlliancesToShow(AllianceTracker tracker) {
228239
}
229240

230241
/*
231-
* Re-calcs the underlying data in a lazy manner.
242+
* Re-calculations the underlying data in a lazy manner.
232243
* Limitation: This is not a thread-safe implementation.
233244
*/
234245
@Override
@@ -287,33 +298,40 @@ class TechTableModel extends AbstractTableModel {
287298
for (int i = 0; i < colList.length; i++) {
288299
colMap.put(colList[i], i + 1);
289300
}
301+
data = getDataAndInitRowMap();
302+
clearAdvances();
303+
}
304+
305+
private synchronized String[] @NotNull [] getDataAndInitRowMap() {
306+
final String[][] dataTable;
290307
boolean useTech = false;
291308
// copy so that the object doesn't change underneath us
292-
final GameData gameData = StatPanel.this.gameData;
293-
try (GameData.Unlocker ignored = gameData.acquireReadLock()) {
294-
final int numTechs = TechAdvance.getTechAdvances(gameData.getTechnologyFrontier()).size();
295-
if (gameData.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
309+
final GameData gameDataSync = StatPanel.this.gameData;
310+
try (GameData.Unlocker ignored = gameDataSync.acquireReadLock()) {
311+
final int numTechs =
312+
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier()).size();
313+
if (gameDataSync.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
296314
useTech = true;
297-
data = new String[numTechs + 1][colList.length + 2];
315+
dataTable = new String[numTechs + 1][colList.length + 2];
298316
} else {
299-
data = new String[numTechs][colList.length + 1];
317+
dataTable = new String[numTechs][colList.length + 1];
300318
}
301319
}
302320
/* Load the technology -> row mapping */
303321
int row = 0;
304322
if (useTech) {
305323
rowMap.put("Tokens", row);
306-
data[row][0] = "Tokens";
324+
dataTable[row][0] = "Tokens";
307325
row++;
308326
}
309327
final List<TechAdvance> techAdvances =
310-
TechAdvance.getTechAdvances(gameData.getTechnologyFrontier(), null);
328+
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier(), null);
311329
for (final TechAdvance tech : techAdvances) {
312330
rowMap.put(tech.getName(), row);
313-
data[row][0] = tech.getName();
331+
dataTable[row][0] = tech.getName();
314332
row++;
315333
}
316-
clearAdvances();
334+
return dataTable;
317335
}
318336

319337
private void clearAdvances() {
@@ -334,34 +352,34 @@ private void initColList() {
334352
Arrays.sort(colList);
335353
}
336354

337-
void update() {
355+
synchronized void update() {
338356
clearAdvances();
339357
// copy so that the object doesn't change underneath us
340-
final GameData gameData = StatPanel.this.gameData;
341-
try (GameData.Unlocker ignored = gameData.acquireReadLock()) {
342-
for (final GamePlayer pid : gameData.getPlayerList().getPlayers()) {
358+
final GameData gameDataSync = StatPanel.this.gameData;
359+
try (GameData.Unlocker ignored = gameDataSync.acquireReadLock()) {
360+
for (final GamePlayer pid : gameDataSync.getPlayerList().getPlayers()) {
343361
if (colMap.get(pid.getName()) == null) {
344362
throw new IllegalStateException(
345363
"Unexpected player in GameData.getPlayerList()" + pid.getName());
346364
}
347365
final int col = colMap.get(pid.getName());
348366
int row = 0;
349-
if (gameData.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
367+
if (gameDataSync.getResourceList().getResource(Constants.TECH_TOKENS) != null) {
350368
final int tokens = pid.getResources().getQuantity(Constants.TECH_TOKENS);
351369
data[row][col] = Integer.toString(tokens);
352370
}
353371
final List<TechAdvance> advancesAll =
354-
TechAdvance.getTechAdvances(gameData.getTechnologyFrontier());
372+
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier());
355373
final List<TechAdvance> has =
356-
TechAdvance.getTechAdvances(gameData.getTechnologyFrontier(), pid);
374+
TechAdvance.getTechAdvances(gameDataSync.getTechnologyFrontier(), pid);
357375
for (final TechAdvance advance : advancesAll) {
358376
if (!has.contains(advance)) {
359377
row = rowMap.get(advance.getName());
360378
data[row][col] = "-";
361379
}
362380
}
363381
for (final TechAdvance advance :
364-
TechTracker.getCurrentTechAdvances(pid, gameData.getTechnologyFrontier())) {
382+
TechTracker.getCurrentTechAdvances(pid, gameDataSync.getTechnologyFrontier())) {
365383
row = rowMap.get(advance.getName());
366384
data[row][col] = "X";
367385
}
@@ -378,7 +396,7 @@ public String getColumnName(final int col) {
378396
}
379397

380398
/*
381-
* Recalcs the underlying data in a lazy manner.
399+
* Recalculations the underlying data in a lazy manner.
382400
* Limitation: This is not a thread-safe implementation.
383401
*/
384402
@Override

0 commit comments

Comments
 (0)