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.BorderLayout;
19  import java.awt.Dimension;
20  import java.awt.FlowLayout;
21  import java.awt.Font;
22  import java.awt.Graphics;
23  import java.awt.Graphics2D;
24  import java.awt.Image;
25  import java.awt.Toolkit;
26  import java.awt.event.WindowAdapter;
27  import java.awt.event.WindowEvent;
28  import java.awt.print.PageFormat;
29  import java.awt.print.Printable;
30  import java.awt.print.PrinterException;
31  import java.text.SimpleDateFormat;
32  import java.util.Date;
33  import java.util.HashMap;
34  import java.util.Map;
35  import java.util.logging.Logger;
36  import javax.swing.BorderFactory;
37  import javax.swing.ImageIcon;
38  import javax.swing.InputMap;
39  import javax.swing.JComponent;
40  import javax.swing.JFrame;
41  import javax.swing.JOptionPane;
42  import javax.swing.JPanel;
43  import javax.swing.JRadioButton;
44  import javax.swing.SwingUtilities;
45  import javax.swing.border.TitledBorder;
46  
47  import net.curre.prefcount.App;
48  import net.curre.prefcount.PrefCountRegistry;
49  import net.curre.prefcount.bean.GameResultBean;
50  import net.curre.prefcount.bean.PlayerStatistics;
51  import net.curre.prefcount.bean.Settings;
52  import net.curre.prefcount.event.MainController;
53  import net.curre.prefcount.gui.menu.MenuItemsBean;
54  import net.curre.prefcount.gui.menu.PrefCountMenuBar;
55  import net.curre.prefcount.gui.theme.skin.PrefSkin;
56  import net.curre.prefcount.gui.theme.skin.PrintSkin;
57  import net.curre.prefcount.gui.type.Place;
58  import net.curre.prefcount.gui.type.WindowComponent;
59  import static net.curre.prefcount.gui.type.WindowComponent.DIVISIBLE_BY_N;
60  import static net.curre.prefcount.gui.type.WindowComponent.DIVISIBLE_IGNORE;
61  import static net.curre.prefcount.gui.type.WindowComponent.LENINGRAD;
62  import static net.curre.prefcount.gui.type.WindowComponent.MAIN_3_PLAYERS;
63  import static net.curre.prefcount.gui.type.WindowComponent.MAIN_4_PLAYERS;
64  import static net.curre.prefcount.gui.type.WindowComponent.SOCHINKA;
65  import net.curre.prefcount.service.LafThemeService;
66  import net.curre.prefcount.service.MainService;
67  import net.curre.prefcount.service.SettingsService;
68  import net.curre.prefcount.util.LocaleExt;
69  import net.curre.prefcount.util.Utilities;
70  
71  import info.clearthought.layout.TableLayout;
72  import info.clearthought.layout.TableLayoutConstraints;
73  
74  /**
75   * This class represent the main frame, which contains
76   * the intro message and the score board.
77   * <p/>
78   * Creation date: Mar 8, 2007
79   *
80   * @author Yevgeny Nyden
81   */
82  public class MainWindow extends JFrame implements Printable {
83  
84    /** Private class logger. */
85    private static Logger log = Logger.getLogger(MainWindow.class.toString());
86  
87    /** Reference to the player dialog frame. */
88    public PlayerDialogBaseFrame playerDialogFrame;
89  
90    /** Reference to the main panel object. */
91    protected JPanel mainPanel;
92  
93    /** Reference to the options panel object. */
94    protected JPanel optionsPanel;
95  
96    /** Reference to the score board panel (when the scores are drawn). */
97    protected ScoreBoardPanel scoreBoardPanel;
98  
99    /** Reference to the "3 players" radio buttons. */
100   protected JRadioButton players3Button;
101 
102   /** Reference to the "4 players" radio buttons. */
103   protected JRadioButton players4Button;
104 
105   /** Reference to the menu bar. */
106   private PrefCountMenuBar prefCountMenuBar;
107 
108   /** Default constructor. */
109   public MainWindow() {
110     this(true);
111   }
112 
113   /**
114    * Constructor that sets frame visibility.
115    *
116    * @param isVisible True when the frame should be made visible by default;
117    *                  false when the frame shoudl be invisible instead.
118    */
119   public MainWindow(boolean isVisible) {
120     super(LocaleExt.getString("pref.scoreboard.title"));
121 
122     log.fine("Creating main window");
123 
124     LocaleExt.registerComponent(this, "pref.scoreboard.title");
125 
126     // creating and setting the windows icon
127     Image appImg = Toolkit.getDefaultToolkit().createImage(App.class.getResource("images/PrefCount-16x16.png"));
128     super.setIconImage(appImg);
129 
130     MenuItemsBean menuItemsBean = PrefCountRegistry.getInstance().getMenuItemsBean();
131     super.setResizable(true);
132 
133     // creating the pref type options
134     JRadioButton leningradButton = menuItemsBean.getJRadioButton(LENINGRAD);
135     JRadioButton sochiButton = menuItemsBean.getJRadioButton(SOCHINKA);
136     JPanel prefTypePanel = createOptionSubPanel("pref.scoreboard.prefType.title",
137                                                 new JRadioButton[]{leningradButton, sochiButton});
138 
139     // creating the number of players options
140     players3Button = menuItemsBean.getJRadioButton(MAIN_3_PLAYERS);
141     players4Button = menuItemsBean.getJRadioButton(MAIN_4_PLAYERS);
142     JPanel playerNumberPanel = createOptionSubPanel("pref.scoreboard.players.title",
143                                                     new JRadioButton[]{players3Button, players4Button});
144 
145     // creating the divisibility options
146     JRadioButton divisibleBy3Button = menuItemsBean.getJRadioButton(DIVISIBLE_IGNORE);
147     JRadioButton divisibleBy4Button = menuItemsBean.getJRadioButton(DIVISIBLE_BY_N);
148     JPanel divisibilityPanel = createOptionSubPanel("pref.scoreboard.divisible.title",
149                                                     new JRadioButton[]{divisibleBy3Button, divisibleBy4Button});
150 
151     this.optionsPanel = new JPanel(new FlowLayout());
152     this.optionsPanel.add(prefTypePanel);
153     this.optionsPanel.add(playerNumberPanel);
154     this.optionsPanel.add(divisibilityPanel);
155 
156     this.scoreBoardPanel = new ScoreBoardPanel();
157 
158     super.getContentPane().add(this.optionsPanel, BorderLayout.NORTH);
159     super.getContentPane().add(this.scoreBoardPanel, BorderLayout.CENTER);
160 
161     this.prefCountMenuBar = MainService.addMainWindowMenuBar(this);
162 
163     this.playerDialogFrame = new PlayerDialogBaseFrame(3, this);
164     PrefCountRegistry.getInstance().setPlayerDialogFrame(this.playerDialogFrame);
165 
166     // action listener to save selection in the current settings
167     MainController mainController = new MainController(this, this.playerDialogFrame);
168     menuItemsBean.setActionListener(mainController);
169     PrefCountRegistry.getInstance().setMainController(mainController);
170 
171     // reading the current settings
172     readFromCurrentSettings();
173     initializeNumberOfPlayers();
174 
175     // adding window close listener
176     super.addWindowListener(new WindowAdapter() {
177       /** {@inheritDoc} */
178       @Override
179       public void windowClosing(WindowEvent event) {
180         MainService.doQuit();
181       }
182     });
183 
184     // setting the visibility of the two frames
185     super.setVisible(isVisible);
186     this.playerDialogFrame.setVisible(isVisible);
187   }
188 
189   /** Initializes number of players. */
190   public void initializeNumberOfPlayers() {
191 
192     // determining the number of players and
193     // disabling the player numbers radio buttons
194     int numberOfPlayers = 4;
195     if (this.players3Button.isSelected()) {
196       numberOfPlayers = 3;
197     }
198 
199     this.scoreBoardPanel.initializeNumberOfPlayers(numberOfPlayers);
200 
201     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
202     resultBean.clearResults();
203 
204     // creating the stats
205     Map<Place, PlayerStatistics> stats = new HashMap<Place, PlayerStatistics>();
206     for (Place place : Place.getPlaces(numberOfPlayers)) {
207       PlayerStatistics stat = new PlayerStatistics(resultBean, place);
208       stat.setPlayerName("");
209       stats.put(place, stat);
210     }
211     resultBean.setPlayerStats(stats);
212 
213     InputMap map = this.optionsPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
214     map.clear();
215 
216     this.playerDialogFrame.initializeNumberOfPlayers(numberOfPlayers);
217     this.playerDialogFrame.setVisible(this.isVisible());
218 
219     super.invalidate();
220     super.validate();
221     super.repaint();
222   }
223 
224   /**
225    * Refreshes locale sensitive components.
226    * This method is called on a locale change event.
227    */
228   public void refreshComponents() {
229     SwingUtilities.invokeLater(new Runnable() {
230       /** {@inheritDoc} */
231       public void run() {
232         LocaleExt.fireLocaleChangeEvent();
233         prefCountMenuBar.refreshLanguageIcon();
234         playerDialogFrame.refreshTable();
235         repaint();
236       }
237     });
238   }
239 
240   /** Displays the about information pane. */
241   public void showAboutInfo() {
242     ImageIcon icon = new ImageIcon(App.class.getResource("images/PrefCount-48x48.png"));
243     JOptionPane.showMessageDialog(this,
244                                   LocaleExt.getString("pref.aboutFrame.message"),
245                                   LocaleExt.getString("pref.aboutFrame.title"),
246                                   JOptionPane.INFORMATION_MESSAGE,
247                                   icon);
248   }
249 
250   /**
251    * This method reads values from the current Settings
252    * object, changes the MainWindow's settings accordingly,
253    * and asks the PlayerDialogBasePanel to do the same
254    * (calls PlayerDialogBasePanel.readFromCurrentSettings()).
255    * Settings that are read and reset:
256    * <ul>
257    * <li>Current Look-and-Feel;</li>
258    * <li>MainWindow size;</li>
259    * <li>Option panel's radio buttons;</li>
260    * <li>Menu bar option menu items;</li>
261    * </ul>
262    *
263    * @see PlayerDialogBaseFrame#readFromCurrentSettings()
264    */
265   public void readFromCurrentSettings() {
266     // this is the current settings
267     Settings settings = SettingsService.getSettings();
268     MenuItemsBean menuItemsBean = PrefCountRegistry.getInstance().getMenuItemsBean();
269 
270     // setting the LAF
271     final String lafSkinId = settings.getLafSkinId();
272     SwingUtilities.invokeLater(new Runnable() {
273       public void run() {
274         LafThemeService.getInstance().setLookAndFeel(lafSkinId, true);
275       }
276     });
277 
278     // setting the frame size
279     try {
280       super.setSize(settings.getMainFrameWidth(), settings.getMainFrameHeight());
281     } catch (Exception e) {
282       super.setSize(Settings.DEFAULT_MAIN_FRAME_WIDTH, Settings.DEFAULT_MAIN_FRAME_HEIGHT);
283     }
284 
285     // selecting the game options buttons in the options panel
286     try {
287       WindowComponent comp = WindowComponent.valueOf(settings.getPrefType());
288       menuItemsBean.setSelected(comp, true);
289     } catch (Exception e) {
290       menuItemsBean.setSelected(WindowComponent.valueOf(Settings.DEFAULT_PREF_TYPE), true);
291     }
292     try {
293       WindowComponent comp = WindowComponent.valueOf(settings.getPlayersNumber());
294       menuItemsBean.setSelected(comp, true);
295     } catch (Exception e) {
296       menuItemsBean.setSelected(WindowComponent.valueOf(Settings.DEFAULT_PLAYERS_NUMBER), true);
297     }
298     try {
299       WindowComponent comp = WindowComponent.valueOf(settings.getDivisibleBy());
300       menuItemsBean.setSelected(comp, true);
301     } catch (Exception e) {
302       menuItemsBean.setSelected(WindowComponent.valueOf(Settings.DEFAULT_DIVISIBLE_BY), true);
303     }
304 
305     // asking the player dialog window to read settings
306     playerDialogFrame.readFromCurrentSettings();
307   }
308 
309   /** {@inheritDoc} */
310   public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
311     if (pageIndex > 0) {
312       return (NO_SUCH_PAGE);
313 
314     } else {
315       Graphics2D g2 = (Graphics2D) g;
316       g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
317       final PrefSkin skin = LafThemeService.getInstance().getCurrentSkin();
318 
319       final int height = (int) pageFormat.getImageableHeight();
320       final int width = (int) pageFormat.getImageableWidth();
321       final int y = (int) pageFormat.getImageableY();
322 
323       // drawing the header
324       String header = LocaleExt.getString("pref.print.header");
325       g2.setPaint(skin.getPlayerNameColor());
326       g2.setStroke(skin.getPlayerNameStroke());
327       g2.setFont(skin.getPlayerNameFont());
328       Dimension headerSize = Utilities.determineSizeOfString(g2, header);
329       g2.drawString(header, Utilities.computeCenterX(width, (int) headerSize.getWidth()), y);
330 
331       // drawing the score table only if the final scores are ready
332       int nextY = y + g2.getFontMetrics().getHeight() + 5;
333       g2.translate(0, nextY);
334       GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
335       if (resultBean.isFinalScoresReady()) {
336         JPanel table = this.playerDialogFrame.lastInputPanel.tablePanel;
337         int tableX = Utilities.computeCenterX(width, table.getWidth());
338         g2.translate(tableX, 0);
339         table.paintComponents(g2);
340         g2.translate(-tableX, table.getHeight() + 15);
341       }
342 
343       // drawing the the score board
344       final int minSize = Math.min(height, width) - 50;
345       final PrefSkin printSkin = new PrintSkin();
346       this.scoreBoardPanel.drawScoreBoard(g2, minSize, minSize, Utilities.computeCenterX(width, minSize),
347                                           0, null, printSkin);
348       g2.translate(0, minSize + 15);
349 
350       // drawing the date
351       String date = new SimpleDateFormat("HH:mm, dd MMMM yyyy (EEEE)").format(new Date());
352       g2.setStroke(new BasicStroke(1));
353       g2.setFont(new Font("Arial", Font.ITALIC, 12));
354       Dimension dateSize = Utilities.determineSizeOfString(g2, date);
355       g2.drawString(date, (width - (int) dateSize.getWidth() - 70), 30);
356 
357       return (PAGE_EXISTS);
358     }
359   }
360 
361   /**
362    * Getter for the main window's menu bar.
363    *
364    * @return <code>PrefCountMenuBar</code> reference.
365    */
366   public PrefCountMenuBar getPrefCountMenuBar() {
367     return this.prefCountMenuBar;
368   }
369 
370   /** *********** PRIVATE METHODS *************** */
371 
372   /**
373    * Creates an option panel (one of the three).
374    *
375    * @param titleKey header title key.
376    * @param buttons  radio buttons to add.
377    * @return created panel.
378    */
379   private JPanel createOptionSubPanel(String titleKey,
380                                       JRadioButton[] buttons) {
381     String title = LocaleExt.getString(titleKey);
382     JPanel outerPanel = new JPanel(
383         new TableLayout(new double[][]{{TableLayout.FILL},
384                                        {TableLayout.PREFERRED, TableLayout.PREFERRED}}));
385 
386     TitledBorder titledBorder = BorderFactory.createTitledBorder(title);
387     LocaleExt.registerComponent(titledBorder, titleKey);
388     titledBorder.setTitleFont(new Font("Arial", Font.PLAIN, 11));
389     outerPanel.setBorder(titledBorder);
390 
391     int row = 0;
392     for (JRadioButton button : buttons) {
393       button.setFont(new Font("Arial", Font.PLAIN, 11));
394       outerPanel.add(button, new TableLayoutConstraints(0, row, 0, row,
395                                                         TableLayoutConstraints.LEFT,
396                                                         TableLayoutConstraints.FULL));
397       ++row;
398     }
399 
400     return outerPanel;
401   }
402 
403 }