View Javadoc

1   /**
2    * This program is free software: you can redistribute it and/or modify
3    * it under the terms of the GNU General Public License as published by
4    * the Free Software Foundation, version 3.
5    *
6    * This program is distributed in the hope that it will be useful,
7    * but WITHOUT ANY WARRANTY; without even the implied warranty of
8    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9    * GNU General Public License for more details.
10   *
11   * You should have received a copy of the GNU General Public License
12   * along with this program. If not, see <http://www.gnu.org/licenses/>.
13   */
14  
15  package net.curre.prefcount.gui;
16  
17  import java.awt.BasicStroke;
18  import java.awt.Color;
19  import java.awt.Dimension;
20  import java.awt.Graphics;
21  import java.awt.Graphics2D;
22  import java.awt.Point;
23  import java.awt.Shape;
24  import java.awt.Stroke;
25  import java.awt.event.MouseEvent;
26  import java.awt.geom.Ellipse2D;
27  import java.awt.geom.GeneralPath;
28  import java.awt.geom.Line2D;
29  import java.awt.geom.Point2D;
30  import java.awt.geom.Rectangle2D;
31  import java.util.Map;
32  import javax.swing.ToolTipManager;
33  
34  import net.curre.prefcount.PrefCountRegistry;
35  import net.curre.prefcount.bean.GameResultBean;
36  import net.curre.prefcount.bean.PlayerStatistics;
37  import net.curre.prefcount.bean.TooltipLocationsMap;
38  import net.curre.prefcount.gui.aa.AAJPanel;
39  import net.curre.prefcount.gui.theme.skin.PrefSkin;
40  import net.curre.prefcount.gui.type.Place;
41  import static net.curre.prefcount.gui.type.Place.EAST;
42  import static net.curre.prefcount.gui.type.Place.NORTH;
43  import static net.curre.prefcount.gui.type.Place.SOUTH;
44  import static net.curre.prefcount.gui.type.Place.WEST;
45  import net.curre.prefcount.gui.type.ScoreItem;
46  import static net.curre.prefcount.gui.type.ScoreItem.FINAL_MOUNT;
47  import static net.curre.prefcount.gui.type.ScoreItem.FINAL_SCORE;
48  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_AMNIST_MOUNT;
49  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_FIXED_MOUNT;
50  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_MOUNT;
51  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_NAME;
52  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_NEW_MOUNT;
53  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_POOL;
54  import static net.curre.prefcount.gui.type.ScoreItem.PLAYER_POOL_CLOSED;
55  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_EAST_SALDO;
56  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_NORTH_SALDO;
57  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_SALDO_TOTAL;
58  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_SOUTH_SALDO;
59  import static net.curre.prefcount.gui.type.ScoreItem.WHIST_WEST_SALDO;
60  import net.curre.prefcount.service.LafThemeService;
61  import net.curre.prefcount.util.LocaleExt;
62  import net.curre.prefcount.util.Utilities;
63  
64  import org.apache.commons.lang.StringUtils;
65  
66  /**
67   * Object of this class represents the score board panel
68   * where all players scores and totals are drawn.
69   * <p/>
70   * Created date: Apr 8, 2007
71   *
72   * @author Yevgeny Nyden
73   */
74  public class ScoreBoardPanel extends AAJPanel {
75  
76    /**
77     * Reference to the map that stores/computes
78     * locations for all items on the score board.
79     */
80    private final ScoreBoardLocationsMap locationsMap;
81  
82    /**
83     * Reference to the map that stores locations (shapes)
84     * for the tooltips of all items on the score board.
85     */
86    private final TooltipLocationsMap ttLocationsMap;
87  
88    /** Constructs a new <code>ScoreBoardPanel</code> object. */
89    public ScoreBoardPanel() {
90      this.locationsMap = new ScoreBoardLocationsMap(this);
91      this.ttLocationsMap = new TooltipLocationsMap();
92      ToolTipManager.sharedInstance().registerComponent(this);
93    }
94  
95    /**
96     * {@inheritDoc}
97     *
98     * @throws UnsupportedOperationException If number of player is not supported.
99     */
100   @Override
101   public void paintComponent(Graphics g) {
102 
103     super.paintComponent(g);
104 
105     final int newWidth = getWidth();
106     final int newHeight = getHeight();
107 
108     final PrefSkin skin = LafThemeService.getInstance().getCurrentSkin();
109     drawScoreBoard((Graphics2D) g, newWidth, newHeight, 0, 0, null, skin);
110   }
111 
112   /** {@inheritDoc} */
113   @Override
114   public String getToolTipText(MouseEvent event) {
115 
116     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
117     final Point point = event.getPoint();
118 
119     for (ScoreItem item : ScoreItem.values()) {
120       for (Map.Entry<Place, Shape> entry : this.ttLocationsMap.get(item).entrySet()) {
121 
122         if (entry.getValue().contains(point)) {
123 
124           if (item == PLAYER_NAME) {
125             final Place place = entry.getKey();
126             PlayerStatistics stats = resultBean.getPlayerStats().get(place);
127             String pName = StringUtils.isBlank(stats.getPlayerName())
128                            ? "" : ": " + stats.getPlayerName();
129 
130             return "<HTML>&nbsp;" + LocaleExt.getString(item.key, LocaleExt.getString(place.longKey, "") + pName) + "&nbsp;";
131 
132           } else {
133             Place place = item.isOtherPlace ? item.place : entry.getKey();
134             PlayerStatistics stats = resultBean.getPlayerStats().get(place);
135             String placeStr = LocaleExt.getString(place.longKey, "");
136             String pName = StringUtils.isBlank(stats.getPlayerName()) ?
137                            placeStr : stats.getPlayerName() + " (" + placeStr + ")";
138 
139             return "<HTML>&nbsp;" + LocaleExt.getString(item.key, pName) + "&nbsp;";
140           }
141         }
142       }
143     }
144 
145     return null;
146   }
147 
148   /**
149    * Paints the score board.
150    *
151    * @param g2            graphics context object to use.
152    * @param newWidth      current width.
153    * @param newHeight     current height.
154    * @param offsetX       offset X coordinate.
155    * @param offsetY       offset Y coordunate.
156    * @param playersNumber when this value is not null, only the score board
157    *                      template will be printed (for the specified number
158    * @param skin          pref skin to use.
159    */
160   protected void drawScoreBoard(Graphics2D g2, int newWidth, int newHeight,
161                                 int offsetX, int offsetY, Integer playersNumber, PrefSkin skin) {
162     this.locationsMap.computeLocations(newWidth, newHeight, offsetX, offsetY, false);
163 
164     final int margin = ScoreBoardLocationsMap.MARGIN;
165     final int width = this.locationsMap.width;
166     final int height = this.locationsMap.height;
167     final int centerX = this.locationsMap.centerX;
168     final int centerY = this.locationsMap.centerY;
169     final int twoFifthX = this.locationsMap.twoFifthX;
170     final int threeFifthX = this.locationsMap.threeFifthX;
171     final int twoFifthY = this.locationsMap.twoFifthY;
172     final int threeFifthY = this.locationsMap.threeFifthY;
173     final int whistPoolX = this.locationsMap.whistPoolDividerX;
174     final int whistPoolY = this.locationsMap.whistPoolDividerY;
175     final int poolMountX = this.locationsMap.poolMountDividerX;
176     final int poolMountY = this.locationsMap.poolMountDividerY;
177 
178     // drawing nice borders
179     Color bordColor = Utilities.createDarkerColor(skin.getMainBackgroundColor(), 20);
180     g2.setColor(bordColor);
181     g2.drawRect(4 + offsetX, 4 + offsetY, width - 8, height - 8);
182     bordColor = Utilities.createDarkerColor(bordColor, 20);
183     g2.setColor(bordColor);
184     g2.drawRect(5 + offsetX, 5 + offsetY, width - 10, height - 10);
185 
186     // painting score board backgound
187     g2.setPaint(skin.getBoardBackgroundPaint());
188     g2.fillRect(6 + offsetX, 6 + offsetY, width - 11, height - 11);
189 
190     // player sections lines
191     prepareBoardLinePen(g2, skin);
192     Ellipse2D e = new Ellipse2D.Double();
193     e.setFrameFromCenter(centerX + 1d, centerY, centerX + 32d, centerY + 30d);
194     g2.draw(e);
195 
196     // section borders and players information
197     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
198     Map<Place, PlayerStatistics> statistics = resultBean.getPlayerStats();
199 
200     final int numPlayers = playersNumber == null ? statistics.size() : playersNumber.intValue();
201     switch (numPlayers) {
202       case 3:
203         // drawing players main sections lines
204         g2.setPaint(skin.getMainSectionLinesPain());
205         g2.drawLine(margin + offsetX, height - margin + offsetY, centerX, centerY);         // /
206         g2.drawLine(centerX, centerY, width - margin + offsetX, height - margin + offsetY); // \
207         g2.drawLine(centerX, centerY, centerX, margin + offsetY);                           // |
208         g2.setColor(skin.getBoardLineColor());
209 
210         // drawing field division lines (to separate mount from pool, etc.)
211         g2.drawLine(twoFifthX, whistPoolY, whistPoolX, whistPoolY);             // - (south-bottom)
212         g2.drawLine(twoFifthX, whistPoolY, twoFifthX, margin + offsetY);        // | (west-left)
213         g2.drawLine(whistPoolX, margin + offsetY, whistPoolX, whistPoolY);      // | (east-right)
214         g2.drawLine(threeFifthX, poolMountY, poolMountX, poolMountY);           // - (south-up)
215         g2.drawLine(threeFifthX, poolMountY, threeFifthX, margin + offsetY);    // | (west-right)
216         g2.drawLine(poolMountX, poolMountY, poolMountX, margin + offsetY);      // | (east-left)
217 
218         // drawing whist fields division lines
219         final int upWhistDivY = (height - (twoFifthY / 2)) / 2 + offsetY;
220         g2.drawLine(centerX, height - margin + offsetY, centerX, whistPoolY);         // | (south)
221         g2.drawLine(margin + offsetX, upWhistDivY, twoFifthX, upWhistDivY);           // - (west)
222         g2.drawLine(whistPoolX, upWhistDivY, width - margin + offsetX, upWhistDivY);  // - (east)
223 
224         if (playersNumber == null) {
225           // drawing player 0 information
226           drawPlayerScores(EAST, g2, skin);
227 
228           // drawing player 1 information
229           drawPlayerScores(SOUTH, g2, skin);
230 
231           // drawing player 2 information
232           drawPlayerScores(WEST, g2, skin);
233         }
234 
235         break;
236 
237       case 4:
238         // drawing players main sections lines
239         g2.setPaint(skin.getMainSectionLinesPain());
240         g2.drawLine(margin + offsetX, margin + offsetY, width - margin + offsetX, height - margin + offsetY); // \
241         g2.drawLine(margin + offsetX, height - margin + offsetY, width - margin + offsetX, margin + offsetY); // /
242         g2.setColor(skin.getBoardLineColor());
243 
244         // drawing field division lines (to separate mount from pool, etc.)
245         g2.drawLine(twoFifthX, whistPoolY, whistPoolX, whistPoolY);       // - (bottom whist-pool)
246         g2.drawLine(twoFifthX, whistPoolY, twoFifthX, twoFifthY);         // | (left whist-pool)
247         g2.drawLine(whistPoolX, whistPoolY, whistPoolX, twoFifthY);       // | (right whist-pool)
248         g2.drawLine(twoFifthX, twoFifthY, whistPoolX, twoFifthY);         // - (top whist-pool)
249         g2.drawLine(threeFifthX, poolMountY, poolMountX, poolMountY);     // - (bottom pool-mount)
250         g2.drawLine(threeFifthX, poolMountY, threeFifthX, threeFifthY);   // | (west pool-mount)
251         g2.drawLine(poolMountX, poolMountY, poolMountX, threeFifthY);     // | (right pool-mount)
252         g2.drawLine(threeFifthX, threeFifthY, poolMountX, threeFifthY);   // - (south pool-mount)
253 
254         // drawing whist fields division lines
255         g2.drawLine(margin + offsetX, this.locationsMap.whistDividerY1,
256                     twoFifthX, this.locationsMap.whistDividerY1);                 // - (west-top)
257         g2.drawLine(margin + offsetX, this.locationsMap.whistDividerY2,
258                     twoFifthX, this.locationsMap.whistDividerY2);                 // - (west-bottom)
259         g2.drawLine(whistPoolX, this.locationsMap.whistDividerY1,
260                     width - margin + offsetX, this.locationsMap.whistDividerY1);  // - (east-top)
261         g2.drawLine(whistPoolX, this.locationsMap.whistDividerY2,
262                     width - margin + offsetX, this.locationsMap.whistDividerY2);  // - (east-bottom)
263 
264         g2.drawLine(this.locationsMap.whistDividerX1, twoFifthY,
265                     this.locationsMap.whistDividerX1, margin + offsetY);          // | (north-left)
266         g2.drawLine(this.locationsMap.whistDividerX2, twoFifthY,
267                     this.locationsMap.whistDividerX2, margin + offsetY);          // | (north-right)
268         g2.drawLine(this.locationsMap.whistDividerX1, height - margin + offsetY,
269                     this.locationsMap.whistDividerX1, whistPoolY);                // | (south-left)
270         g2.drawLine(this.locationsMap.whistDividerX2, height - margin + offsetY,
271                     this.locationsMap.whistDividerX2, whistPoolY);                // | (south-right)
272 
273         if (playersNumber == null) {
274           // displaying player 0 information
275           drawPlayerScores(NORTH, g2, skin);
276 
277           // displaying player 1 information
278           drawPlayerScores(EAST, g2, skin);
279 
280           // displaying player 2 information
281           drawPlayerScores(SOUTH, g2, skin);
282 
283           // displaying player 3 information
284           drawPlayerScores(WEST, g2, skin);
285         }
286         break;
287 
288       default:
289         throw new UnsupportedOperationException(statistics.size() + " number of players is NOT supported!");
290     }
291   }
292 
293   /**
294    * Initializes number of players for the player dialog frame.
295    *
296    * @param numberOfPlayers number of players.
297    */
298   public void initializeNumberOfPlayers(int numberOfPlayers) {
299     this.getLocationsMap().initialize(numberOfPlayers);
300     this.ttLocationsMap.clear();
301   }
302 
303   /**
304    * Gets the location map object.
305    *
306    * @return the location map.
307    */
308   public synchronized ScoreBoardLocationsMap getLocationsMap() {
309     return this.locationsMap;
310   }
311 
312   /***************** PRIVATE METHODS *******************/
313 
314   /**
315    * Method to draw all player scores.
316    *
317    * @param place Current player place.
318    * @param g2    Graphics object to use.
319    * @param skin  Current skin.
320    */
321   private void drawPlayerScores(Place place, Graphics2D g2, PrefSkin skin) {
322 
323     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
324     PlayerStatistics stats = resultBean.getPlayerStats().get(place);
325     Map<ScoreItem, Point2D.Double> locations = getLocationsMap().getLocationsMap(place);
326 
327     // drawing player's place letter (S-outh, E-ast...)
328     preparePlayerNamePen(g2, skin);
329     Point.Double point = locations.get(PLAYER_NAME);
330     String placeChar = LocaleExt.getString(place.shortKey);
331     g2.drawString(placeChar, (float) point.getX(), (float) point.getY());
332     this.ttLocationsMap.addRectangleLocation(PLAYER_NAME, place, g2, point, placeChar);
333 
334     // drawing player's mount value
335     preparePlayerScorePen(g2, skin);
336     point = locations.get(PLAYER_MOUNT);
337     final String mount = getStringFromInt(stats.getMountain());
338     g2.drawString(mount, (float) point.getX(), (float) point.getY());
339     this.ttLocationsMap.addRectangleLocation(PLAYER_MOUNT, place, g2, point, mount);
340 
341     // drawing player's pool value
342     point = locations.get(PLAYER_POOL);
343     final String pool = getStringFromInt(stats.getPool());
344     g2.drawString(pool, (float) point.getX(), (float) point.getY());
345     this.ttLocationsMap.addRectangleLocation(PLAYER_POOL, place, g2, point, pool);
346 
347     // drawing player's whist values
348     drawWhistAndWhistFixes(g2, place, stats, locations);
349 
350     // drawing players computed score values if the final scores are ready
351     if (resultBean.isFinalScoresReady()) {
352       // whist saldo
353       for (ScoreItem other : getLocationsMap().getOtherWhistSaldoItems(place)) {
354         final int wSaldo = stats.getWhistSaldoMap().get(other.place).intValue();
355         drawWhistSaldo(g2, wSaldo, locations.get(other), false, place, other);
356       }
357 
358       // total whist saldo
359       final int wSaldo = stats.getWhistSaldoAgainstPlayer(place).intValue();
360       drawWhistSaldo(g2, wSaldo, locations.get(WHIST_SALDO_TOTAL), true, place, WHIST_SALDO_TOTAL);
361 
362       // new mountain and new (closed) pool
363       final boolean isVertical = (place == EAST || place == WEST);
364       drawNewMountain(g2, stats, locations.get(PLAYER_MOUNT), isVertical, place);
365       drawClosedPool(g2, stats, locations.get(PLAYER_POOL), isVertical, place);
366 
367       // final mountain and final scores 
368       preparePlayerTotalsPen(g2, skin);
369       drawFinalMountain(g2, stats, locations.get(FINAL_MOUNT), place);
370       drawFinalScore(g2, stats, locations.get(FINAL_SCORE), skin, place);
371 
372     } else {
373       this.ttLocationsMap.removeLocation(place, WHIST_SALDO_TOTAL, WHIST_EAST_SALDO, WHIST_SOUTH_SALDO,
374                                          WHIST_WEST_SALDO, WHIST_NORTH_SALDO, PLAYER_NEW_MOUNT,
375                                          PLAYER_AMNIST_MOUNT, PLAYER_FIXED_MOUNT, PLAYER_POOL_CLOSED,
376                                          FINAL_MOUNT, FINAL_SCORE);
377     }
378   }
379 
380   /**
381    * Draws a number with an oval or a rectangle around it at the
382    * given position (x, y) - used for displaying players whist saldo.
383    *
384    * @param g2         Graphics2D to use.
385    * @param saldo      Number to draw.
386    * @param point      Coordinates.
387    * @param totalSaldo True indicates that this is a total saldo,
388    *                   therefore, a rectangle is drawn around the number;
389    *                   false draws an oval instead.
390    * @param place      player's place.
391    * @param item       score board item.
392    */
393   private void drawWhistSaldo(Graphics2D g2, final int saldo, final Point2D.Double point,
394                               boolean totalSaldo, Place place, ScoreItem item) {
395     final String str = String.valueOf(saldo);
396     final int width = 16 + str.length() * 14;
397     final int shapeX = (int) point.x - (10 + (saldo < 0 ? 4 : 0));
398     final int shapeY = (int) point.y - 16;
399     g2.drawString(str, (int) point.x, (int) point.y);
400     Shape shape;
401     if (totalSaldo) {
402       shape = new Rectangle2D.Double(shapeX, shapeY, width, 20);
403       g2.draw(shape);
404 
405     } else {
406       shape = new Ellipse2D.Double(shapeX, shapeY, width, 20);
407       g2.drawOval(shapeX, (int) point.y - 16, width, 20);
408     }
409 
410     // adding the tooltip location
411     this.ttLocationsMap.addShapeLocation(item, place, shape);
412   }
413 
414   /**
415    * Draws new pool value (the closed pool value).
416    *
417    * @param g2       Graphics2D to use.
418    * @param stats    Player statistics object.
419    * @param point    Coordinates of the original pool string.
420    * @param vertical If true, indicates vertical positioning
421    * @param place    player's place.
422    */
423   private void drawClosedPool(Graphics2D g2, PlayerStatistics stats,
424                               final Point2D.Double point, final boolean vertical, Place place) {
425     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
426     final String str = getStringFromInt(resultBean.getMaxPool());
427     final float width = (float) Utilities.determineSizeOfString(g2, stats.getPool().toString()).getWidth();
428     final float x = (float) point.getX();
429     final float y = (float) point.getY();
430     Stroke tempStroke = g2.getStroke();
431     g2.setStroke(new BasicStroke(2));
432     g2.draw(new Line2D.Float(x, y - 2f, x + width + 2f, y - 8f));
433     g2.setStroke(tempStroke);
434 
435     Point2D.Double bPoint;
436     if (vertical) {
437       g2.drawString(str, x, y + 20f);
438       bPoint = new Point2D.Double(x, y + 20d);
439 
440     } else {
441       final float mX = x + 7f + width;
442       g2.drawString(str, mX, y);
443       bPoint = new Point2D.Double(mX, y);
444     }
445 
446     this.ttLocationsMap.addRectangleLocation(PLAYER_POOL_CLOSED, place, g2, bPoint, str);
447   }
448 
449   /**
450    * Draws new mountain values (mount after closed pool,
451    * amnisted mountain, and the mount fix if any).
452    *
453    * @param g2       Graphics2D to use.
454    * @param stats    Player statistics object.
455    * @param point    Coordinates of the original mountain string.
456    * @param vertical If true, indicates vertical positioning
457    * @param place    player's place.
458    */
459   private void drawNewMountain(Graphics2D g2, PlayerStatistics stats,
460                                final Point2D.Double point, final boolean vertical, Place place) {
461     int newMount = stats.getNewMountain();
462     final String newMountStr = getStringFromInt(Integer.valueOf(newMount));
463     final float width1 = (float) Utilities.determineSizeOfString(g2, stats.getMountain().toString()).getWidth();
464     final int amnistMount = newMount - stats.getMinMountain();
465     final String amnistMountStr = amnistMount + ".";
466     final String mountFixStr = stats.getMountFix() == null ? null :
467                                (amnistMount + stats.getMountFix().intValue()) + ".";
468     final float x = (float) point.getX();
469     final float y = (float) point.getY();
470     Stroke tempStroke = g2.getStroke();
471     g2.setStroke(new BasicStroke(2));
472     g2.draw(new Line2D.Float(x, y - 2f, x + width1 + 2f, y - 8f));
473     g2.setStroke(tempStroke);
474 
475     Point2D.Double newMountPoint;
476     Point2D.Double amnistMountPoint;
477     Point2D.Double fixMountPoint = null;
478     if (vertical) {
479       g2.drawString(newMountStr, x, y + 20f);
480       newMountPoint = new Point2D.Double(x, y + 20d);
481       g2.drawString(amnistMountStr, x, y + 40f);
482       amnistMountPoint = new Point2D.Double(x, y + 40d);
483       if (mountFixStr != null) {
484         g2.drawString(mountFixStr, x, y + 60f);
485         fixMountPoint = new Point2D.Double(x, y + 60d);
486       }
487     } else {
488       final float mountX = x + width1 + 7f;
489       final float width2 = (float) Utilities.determineSizeOfString(g2, newMountStr).getWidth();
490       g2.drawString(newMountStr, mountX, y);
491       newMountPoint = new Point2D.Double(mountX, y);
492       final float amnX = mountX + width2 + 2f;
493       g2.drawString(amnistMountStr, amnX, y);
494       amnistMountPoint = new Point2D.Double(amnX, y);
495       if (mountFixStr != null) {
496         final float width3 = (float) Utilities.determineSizeOfString(g2, mountFixStr).getWidth();
497         final float fixX = mountX + width2 + width3 + 4f;
498         g2.drawString(mountFixStr, fixX, y);
499         fixMountPoint = new Point2D.Double(fixX, y);
500       }
501     }
502 
503     // adding tooltip locations
504     this.ttLocationsMap.addRectangleLocation(PLAYER_NEW_MOUNT, place, g2, newMountPoint, newMountStr);
505     this.ttLocationsMap.addRectangleLocation(PLAYER_AMNIST_MOUNT, place, g2, amnistMountPoint, amnistMountStr);
506     if (fixMountPoint == null) {
507       this.ttLocationsMap.removeLocation(place, PLAYER_FIXED_MOUNT);
508     } else {
509       this.ttLocationsMap.addRectangleLocation(PLAYER_FIXED_MOUNT, place, g2, fixMountPoint, mountFixStr);
510     }
511   }
512 
513   /**
514    * Draws new mountain value.
515    *
516    * @param g2    Graphics2D to use.
517    * @param stats Player statistics object.
518    * @param point Point's coordinates.
519    * @param place player's place.
520    */
521   private void drawFinalMountain(Graphics2D g2, PlayerStatistics stats,
522                                  final Point2D.Double point, Place place) {
523     int mount = stats.getFinalMountainInWhists();
524     final String str = String.valueOf(mount);
525     final int width = 16 + str.length() * 14;
526     final int adjustX = 10 + (mount < 0 ? 4 : 0);
527     final float x = (float) point.getX();
528     final float y = (float) point.getY();
529     g2.drawString(str, x, y);
530     Shape shape = new Rectangle2D.Double(x - adjustX, y - 16d, width, 20d);
531     g2.draw(shape);
532 
533     // adding the tooltip location
534     this.ttLocationsMap.addShapeLocation(FINAL_MOUNT, place, shape);
535   }
536 
537   /**
538    * Draws final score and a thombus around it
539    * at the given location.
540    *
541    * @param g2    Graphics2D to use.
542    * @param stats Player statistics object.
543    * @param point Point's coordinates.
544    * @param skin  current skin.
545    * @param place player's place.
546    */
547   private void drawFinalScore(Graphics2D g2, PlayerStatistics stats,
548                               final Point2D.Double point, PrefSkin skin, Place place) {
549     final float x = (float) point.getX();
550     final float y = (float) point.getY();
551     final String score = String.valueOf(stats.getFinalScoreInWhists());
552     g2.drawString(score, x, y);
553 
554     final Dimension corrSize = Utilities.determineSizeOfString(g2, score);
555     final float halfWidth = (float) corrSize.getWidth() / 2;
556     final float halfHeight = (float) corrSize.getHeight() / 2;
557     final float realCenterX = x + halfWidth;
558     final float realCenterY = y - halfHeight + 5f;
559     final float leftX = realCenterX - halfWidth - 15f;
560     final float rightX = realCenterX + halfWidth + 15f;
561     final float upY = realCenterY - halfHeight - 15f;
562     final float downY = realCenterY + halfHeight + 15f;
563 
564     GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 4);
565     polygon.moveTo(realCenterX, upY);
566     polygon.lineTo(rightX, realCenterY);
567     polygon.lineTo(realCenterX, downY);
568     polygon.lineTo(leftX, realCenterY);
569     polygon.closePath();
570     g2.draw(polygon);
571     g2.setPaint(skin.getFinalScoreBackgroundPaint());
572     g2.fill(polygon);
573 
574     // adding the tooltip location
575     this.ttLocationsMap.addShapeLocation(FINAL_SCORE, place, polygon);
576   }
577 
578   /**
579    * Converts passed Integer to a String appending a '.'
580    * ant the end; if the passed value is null an empty string
581    * is returned.
582    *
583    * @param value Inetger value to convert.
584    * @return String representation of the passed integer
585    *         with a period at the end or an empty string
586    *         if the passed integer is null.
587    */
588   private static String getStringFromInt(final Integer value) {
589     return value == null ? "" : value.toString() + ".";
590   }
591 
592   /**
593    * Sets the color and stroke on the graphics object
594    * for the score board lines painting.
595    *
596    * @param g2   Graphics object.
597    * @param skin Current prefcount skin.
598    */
599   private static void prepareBoardLinePen(Graphics2D g2, PrefSkin skin) {
600     g2.setStroke(skin.getBoardLineStroke());
601     g2.setColor(skin.getBoardLineColor());
602   }
603 
604   /**
605    * Sets the color, stroke and font on the graphics
606    * object for the player name painting.
607    *
608    * @param g2   Graphics object.
609    * @param skin Current prefcount skin.
610    */
611   private static void preparePlayerNamePen(Graphics2D g2, PrefSkin skin) {
612     g2.setColor(skin.getPlayerNameColor());
613     g2.setFont(skin.getPlayerNameFont());
614     g2.setStroke(skin.getPlayerNameStroke());
615   }
616 
617   /**
618    * Sets the color, stroke and font on the graphics
619    * object for the player score painting.
620    *
621    * @param g2   Graphics object.
622    * @param skin Current prefcount skin.
623    */
624   private static void preparePlayerScorePen(Graphics2D g2, PrefSkin skin) {
625     g2.setColor(skin.getPlayerScoreColor());
626     g2.setFont(skin.getPlayerScoreFont());
627     g2.setStroke(skin.getPlayerScoreStroke());
628   }
629 
630   /**
631    * Sets the color, stroke and font on the graphics
632    * object for the player (score) totals painting.
633    *
634    * @param g2   Graphics object.
635    * @param skin Current prefcount skin.
636    */
637   private static void preparePlayerTotalsPen(Graphics2D g2, PrefSkin skin) {
638     g2.setColor(skin.getPlayerTotalsColor());
639     g2.setFont(skin.getPlayerTotalsFont());
640     g2.setStroke(skin.getPlayerTotalsStroke());
641   }
642 
643   /**
644    * Draws player's whists and whist fixes.
645    *
646    * @param g2        graphics object.
647    * @param place     players place.
648    * @param stats     players' statistics bean.
649    * @param locations locations map.
650    */
651   private void drawWhistAndWhistFixes(Graphics2D g2, Place place, PlayerStatistics stats,
652                                       Map<ScoreItem, Point2D.Double> locations) {
653     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
654     Point.Double point;
655     for (ScoreItem other : getLocationsMap().getOtherWhistItems(place)) {
656       point = locations.get(other);
657       String whistStr = stats.getWhistsStringForPlayer(other.place);
658       g2.drawString(whistStr, (float) point.getX(), (float) point.getY());
659       this.ttLocationsMap.addRectangleLocation(other, place, g2, point, whistStr);
660 
661       // drawing the whist fixes if any and if the final score is ready
662       final ScoreItem otherFix = ScoreItem.getWhistFixForWhist(other.place);
663       if (resultBean.isFinalScoresReady()) {
664         Integer fix = stats.getWhistFixesMap().get(other.place);
665         if (fix != null) {
666           String fixStr = (stats.getWhistsAgainstPlayer(other.place).intValue() + fix.intValue()) + ".";
667           float fixX = (float) (point.getX() + 2 + Utilities.determineSizeOfString(g2, whistStr).getWidth());
668           g2.drawString(fixStr, fixX, (float) point.getY());
669           Point2D.Double fPoint = new Point2D.Double(fixX, point.getY());
670           this.ttLocationsMap.addRectangleLocation(otherFix, place, g2, fPoint, fixStr);
671 
672         } else {
673           this.ttLocationsMap.removeLocation(place, otherFix);
674         }
675 
676       } else {
677         this.ttLocationsMap.removeLocation(place, otherFix);
678       }
679     }
680   }
681 
682 }