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 javax.swing.JButton;
18  import javax.swing.JComponent;
19  import javax.swing.JFrame;
20  import javax.swing.JLabel;
21  import javax.swing.JPanel;
22  import javax.swing.KeyStroke;
23  import java.awt.BorderLayout;
24  import java.awt.CardLayout;
25  import java.awt.Color;
26  import java.awt.Component;
27  import java.awt.Container;
28  import java.awt.FlowLayout;
29  import java.awt.Image;
30  import java.awt.Toolkit;
31  import java.awt.event.WindowAdapter;
32  import java.awt.event.WindowEvent;
33  import java.util.Map;
34  
35  import net.curre.prefcount.App;
36  import net.curre.prefcount.PrefCountRegistry;
37  import net.curre.prefcount.bean.GameResultBean;
38  import net.curre.prefcount.bean.Settings;
39  import net.curre.prefcount.event.QuitActionListener;
40  import net.curre.prefcount.gui.aa.AAJLabel;
41  import net.curre.prefcount.gui.aa.AAJPanel;
42  import net.curre.prefcount.gui.menu.MenuItemsBean;
43  import net.curre.prefcount.gui.menu.PrefCountMenuBar;
44  import net.curre.prefcount.gui.type.Place;
45  import static net.curre.prefcount.gui.type.Place.EAST;
46  import static net.curre.prefcount.gui.type.Place.NORTH;
47  import static net.curre.prefcount.gui.type.Place.SOUTH;
48  import static net.curre.prefcount.gui.type.Place.WEST;
49  import static net.curre.prefcount.gui.type.WindowComponent.DIALOG_BACK;
50  import static net.curre.prefcount.gui.type.WindowComponent.DIALOG_FORWARD;
51  import net.curre.prefcount.service.MainService;
52  import net.curre.prefcount.service.SettingsService;
53  import net.curre.prefcount.util.LocaleExt;
54  import net.curre.prefcount.util.Utilities;
55  
56  /**
57   * Object of this class represents a player question panel
58   * wrapper - frame with navigation buttons and messages
59   * fields common to all player input dialog windows;
60   * <p/>
61   * panel objects that represent individual input steps
62   * go into the questionsPane, which has a card layout.
63   * <p/>
64   * Created date: May 6, 2007
65   *
66   * @author Yevgeny Nyden
67   */
68  public class PlayerDialogBaseFrame extends JFrame {
69  
70    /** Reference to the main window object. */
71    protected MainWindow mainWindow;
72  
73    /** Reference to the player dialog frame's menu bar. */
74    protected PrefCountMenuBar menuBar;
75  
76    /** Navigation - back button. */
77    JButton backButton;
78  
79    /** Navigation - forward/next button. */
80    JButton nextButton;
81  
82    /** Reference to the (intro) message label. */
83    JLabel messageLabel;
84  
85    /** Reference to the (validation) error label. */
86    JLabel errorLabel;
87  
88    /** Reference to the questions pane. */
89    JPanel questionsPane;
90  
91    /** Number of players in the game. */
92    protected int playersNumber;
93  
94    /** Index of the current player panel. */
95    private int currPlayerPanel;
96  
97    /** Reference to the players' names panel. */
98    private PlayersNamesPanel playerNamesPanel;
99  
100   /** Reference to the last (score) panel. */
101   LastInputPanel lastInputPanel;
102 
103   /**
104    * Constructor with parameters. Note that the visibility of
105    * this frame will depend on the visibility of the main window
106    * frame. This frame will be visible if the main window frame
107    * is visible; false otherwise.
108    *
109    * @param playersNumber Number of players.
110    * @param mainWindow    Reference to the main window object.
111    */
112   public PlayerDialogBaseFrame(int playersNumber, MainWindow mainWindow) {
113 
114     super(LocaleExt.getString("pref.dialog.title"));
115     LocaleExt.registerComponent(this, "pref.dialog.title");
116 
117     Image appImg = Toolkit.getDefaultToolkit().createImage(App.class.getResource("images/PrefCount-16x16.png"));
118     super.setIconImage(appImg);
119 
120     this.playersNumber = playersNumber;
121     this.mainWindow = mainWindow;
122 
123     this.messageLabel = new AAJLabel();
124     LocaleExt.registerComponent(this.messageLabel, "pref.dialog.message.default");
125     this.questionsPane = new AAJPanel();
126     this.errorLabel = new AAJLabel();
127     LocaleExt.registerComponent(this.errorLabel, "pref.dialog.message.default");
128 
129     LocaleExt.registerComponent(new LocaleExt.LocaleExec() {
130       public void doChange() {
131         getCurrentInnerPanel().setHeaderMessage(messageLabel);
132       }
133     }, "DIALOG_HEADER_UPDATER");
134 
135     initComponents();
136 
137     super.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
138     super.setLocationRelativeTo(getOwner());
139 
140     // focusing the first panel's first input text field
141     super.addWindowListener(new WindowAdapter() {
142       /** {@inheritDoc} */
143       @Override
144       public void windowOpened(WindowEvent e) {
145         getCurrentInnerPanel().focusFirstInputField();
146       }
147 
148       /** {@inheritDoc} */
149       @Override
150       public void windowClosing(WindowEvent event) {
151         PlayerDialogBaseFrame.this.mainWindow.getPrefCountMenuBar().setDialogFrameItemState(false);
152         if (PlayerDialogBaseFrame.this.menuBar != null) {
153           PlayerDialogBaseFrame.this.menuBar.setDialogFrameItemState(false);
154         }
155       }
156     });
157 
158     // adding a menu bar (only when running on Mac OS)
159     this.menuBar = MainService.addPlayerDialogMenuBar(this);
160   }
161 
162   /**
163    * Initializes number of players. This method should be called
164    * together with the MainWindow's initializeNumberOfPlayers()
165    * method.
166    *
167    * @param numberOfPlayers number of players in the game.
168    * @see net.curre.prefcount.gui.MainWindow#initializeNumberOfPlayers()
169    */
170   public void initializeNumberOfPlayers(int numberOfPlayers) {
171     this.playersNumber = numberOfPlayers;
172 
173     initInputDialogs();
174   }
175 
176   /**
177    * Getter for the window's menu bar.
178    *
179    * @return <code>PrefCountMenuBar</code> reference.
180    */
181   public PrefCountMenuBar getPrefCountMenuBar() {
182     return this.menuBar;
183   }
184 
185   /**
186    * Next or Previous button action helper.
187    *
188    * @param isNextAction True if we are switching to the next panel;
189    *                     false if previous.
190    */
191   public void nextQuestionEventHelper(boolean isNextAction) {
192     if ((isNextAction && this.nextButton.isEnabled() == false) ||
193         (isNextAction == false && this.backButton.isEnabled() == false)) {
194       return;
195     }
196     DialogInnerPanel currPanel = getCurrentInnerPanel();
197 
198     // validating the current panel if necessary
199     if (currPanel.validateFields() == false) {
200       return;
201     }
202 
203     // do necessary activity, and move to the next panel
204     currPanel.doOnLeave();
205     this.currPlayerPanel = this.currPlayerPanel + (isNextAction ? 1 : -1);
206     currPanel = getCurrentInnerPanel();
207     currPanel.doOnEntry();
208 
209     // enabling/disabling navigation buttons and menu items
210     final boolean nextButtonEnabled = currPanel.isLastPanel() == false;
211     final boolean backButtonEnabled = currPanel.isFirstPanel() == false;
212     final boolean computeButtonEnabled = nextButtonEnabled == false;
213     this.nextButton.setEnabled(nextButtonEnabled);
214     this.backButton.setEnabled(backButtonEnabled);
215     if (this.menuBar != null) {
216       this.menuBar.toggleNextAction(nextButtonEnabled);
217       this.menuBar.toggleBackAction(backButtonEnabled);
218       this.menuBar.toggleComputeAction(computeButtonEnabled);
219     }
220 
221     // changing the main label
222     currPanel.setHeaderMessage(this.messageLabel);
223 
224     CardLayout clay = (CardLayout) this.questionsPane.getLayout();
225     if (isNextAction) {
226       clay.next(this.questionsPane);  // switching to the next panel
227     } else {
228       clay.previous(this.questionsPane); // switching to the previous panel
229     }
230     currPanel.focusFirstInputField();
231     this.mainWindow.repaint();      // repainting the score board
232   }
233 
234   /**
235    * Returns reference to the current inner panel.
236    *
237    * @return Reference to the current inner panel.
238    */
239   public DialogInnerPanel getCurrentInnerPanel() {
240     return (DialogInnerPanel) this.questionsPane.getComponents()[this.currPlayerPanel];
241   }
242 
243   /**
244    * Getter for property 'currPlayerPanel' -
245    * index of the current player panel.
246    *
247    * @return Value for property 'currPlayerPanel'.
248    */
249   public int getCurrPlayerPanel() {
250     return this.currPlayerPanel;
251   }
252 
253   /**
254    * This method reads values from the current Settings
255    * object and changes the PlayerDialogBasePanel's settings accordingly.
256    * It should be called together with the MainMainWindow's
257    * readFromCurrentSettings() method.
258    * Settings that are read and reset:
259    * <ul>
260    * <li>PlayerDialogBasePanel size;</li>
261    * </ul>
262    *
263    * @see MainWindow#readFromCurrentSettings()
264    */
265   public void readFromCurrentSettings() {
266     // this is the current settings
267     Settings settings = SettingsService.getSettings();
268 
269     // setting the frame size
270     super.setSize(settings.getDialogFrameWidth(), settings.getDialogFrameHeight());
271   }
272 
273   /**
274    * Checks if there is some data entered into the
275    * player dialog (at least one player name is not blank).
276    *
277    * @return true if at least one player name is not blank; false otherwise.
278    */
279   public boolean isSomeDataEntered() {
280     return playerNamesPanel != null && playerNamesPanel.isSomeDataEntered();
281   }
282 
283   /**
284    * Refreshes the score table - updates the header
285    * labels according to the current locale.
286    */
287   public void refreshTable() {
288     this.lastInputPanel.refreshTable();
289   }
290 
291   /**
292    * Sets/hides the error label.
293    *
294    * @param messageKey Error message key or null if the error
295    *                   label should be hidden.
296    */
297   void toggleErrorField(String messageKey) {
298     if (messageKey == null) {
299       errorLabel.setText("");
300       errorLabel.setVisible(false);
301     } else {
302       errorLabel.setText(LocaleExt.getString(messageKey));
303       errorLabel.setVisible(true);
304       LocaleExt.reregisterComponent(errorLabel, messageKey);
305     }
306   }
307 
308   /**
309    * Sets players names in the GameResultBean bean.
310    *
311    * @param playersNames map with player names.
312    */
313   void setPlayersNames(Map<Place, String> playersNames) {
314     GameResultBean resultBean = PrefCountRegistry.getInstance().getGameResultBean();
315     for (Map.Entry<Place, String> entry : playersNames.entrySet()) {
316       String name = entry.getValue();
317       resultBean.getPlayerStats().get(entry.getKey()).setPlayerName(name);
318     }
319   }
320 
321   /** Private methods ********************** */
322 
323   /** Initializes the components. */
324   private void initComponents() {
325 
326     MenuItemsBean menuItemsBean = PrefCountRegistry.getInstance().getMenuItemsBean();
327 
328     // adding Ctrl-Q - quit shortcut to the panel if not on Mac OS;
329     // Mac OS will have a menu for every frame (with a Quit shortcut)
330     JPanel mainContentPanel = new JPanel();
331     mainContentPanel.setOpaque(false);
332     if (Utilities.isMacOs() == false) {
333       mainContentPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
334           .put(KeyStroke.getKeyStroke("control Q"), "exitAppAction");
335       mainContentPanel.getActionMap().put("exitAppAction", new QuitActionListener());
336     }
337 
338     //======== this ========
339     Container contentPaneOut = getContentPane();
340     contentPaneOut.setLayout(new BorderLayout(5, 5));
341 
342     // creating and initializing the navigation panel
343     JPanel navigationPanel = new JPanel();
344     {
345       navigationPanel.setOpaque(false);
346       navigationPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 10));
347 
348       // adding the button shortcuts (only if we are not running on Mac OS);
349       // we assume here, that for Mac OS, all button shortcuts are added to
350       // the frame's menu bar
351       JPanel pane = Utilities.isMacOs() ? null : this.questionsPane;
352 
353       //---- backButton ----
354       this.backButton = menuItemsBean.getJButton(DIALOG_BACK, pane);
355       this.backButton.setEnabled(false);
356       LocaleExt.registerComponent(this.backButton, DIALOG_BACK);
357       navigationPanel.add(this.backButton);
358       navigationPanel.add(new JPanel(null));
359 
360       //---- nextButton ----
361       this.nextButton = menuItemsBean.getJButton(DIALOG_FORWARD, pane);
362       this.nextButton.setEnabled(true);
363       LocaleExt.registerComponent(this.nextButton, DIALOG_FORWARD);
364       navigationPanel.add(this.nextButton);
365     }
366     contentPaneOut.add(navigationPanel, BorderLayout.NORTH);
367 
368     //======== mainContentPanel ========
369     {
370       mainContentPanel.setLayout(new BorderLayout());
371 
372       //======== messagePanel ========
373       JPanel messagePanel = new JPanel();
374       {
375         messagePanel.setOpaque(false);
376         messagePanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
377 
378         //---- messageLabel ----
379         messageLabel.setText(LocaleExt.getString("pref.dialog.message.default"));
380         messagePanel.add(messageLabel);
381       }
382       mainContentPanel.add(messagePanel, BorderLayout.NORTH);
383 
384       JPanel contentPaneWrapper = new JPanel();
385       //======== contentPane ========
386       {
387         contentPaneWrapper.setLayout(new FlowLayout());
388         questionsPane.setLayout(new CardLayout());
389         contentPaneWrapper.add(questionsPane);
390       }
391       mainContentPanel.add(contentPaneWrapper, BorderLayout.CENTER);
392     }
393     contentPaneOut.add(mainContentPanel, BorderLayout.CENTER);
394 
395     // creating and initializing the error panel and label
396     JPanel errorPanel = new JPanel();
397     {
398       errorPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 10));
399       errorPanel.setOpaque(false);
400       this.errorLabel.setText(LocaleExt.getString("pref.dialog.errorLabel.default"));
401       this.errorLabel.setForeground(Color.RED);
402       this.errorLabel.setVisible(false);
403       this.errorLabel.setOpaque(false);
404       errorPanel.add(this.errorLabel);
405     }
406     contentPaneOut.add(errorPanel, BorderLayout.SOUTH);
407 
408     setLocationRelativeTo(getOwner());
409   }
410 
411   /**
412    * Creates input dialog components and
413    * adds them to the questionPane.
414    */
415   private void initInputDialogs() {
416     // resetting buttons and labels; removing error message (if any)
417     toggleErrorField(null);
418     this.currPlayerPanel = 0;
419     this.backButton.setEnabled(false);
420     this.nextButton.setEnabled(true);
421     if (this.questionsPane.getComponentCount() > 0) {
422       for (Component c : this.questionsPane.getComponents()) {
423         if (c instanceof DialogInnerPanel) {
424           ((DialogInnerPanel) c).unregisterLocaleSensitiveComponents();
425         }
426       }
427       this.questionsPane.removeAll();
428     }
429 
430     this.playerNamesPanel = new PlayersNamesPanel(this, this.playersNumber);
431     this.playerNamesPanel.setHeaderMessage(this.messageLabel);
432     this.questionsPane.add(this.playerNamesPanel, "Player Names");
433     this.questionsPane.add(new PlayerDataPanel(this, this.playersNumber, EAST), EAST.name());
434     this.questionsPane.add(new PlayerDataPanel(this, this.playersNumber, SOUTH), SOUTH.name());
435     this.questionsPane.add(new PlayerDataPanel(this, this.playersNumber, WEST), WEST.name());
436     if (this.playersNumber == 4) {
437       this.questionsPane.add(new PlayerDataPanel(this, this.playersNumber, NORTH), NORTH.name());
438     }
439 
440     this.lastInputPanel = new LastInputPanel(this);
441     PrefCountRegistry.getInstance().setLastInputPanel(this.lastInputPanel);
442     this.questionsPane.add(this.lastInputPanel, "Last Input");
443   }
444 
445 }