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.*;
18  import java.awt.event.WindowAdapter;
19  import java.awt.event.WindowEvent;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.ResourceBundle;
23  import javax.swing.*;
24  
25  import net.curre.prefcount.App;
26  import net.curre.prefcount.PrefCountRegistry;
27  import net.curre.prefcount.bean.PlayerStatistics;
28  import net.curre.prefcount.bean.Settings;
29  import net.curre.prefcount.event.DialogButtonNavigationListener;
30  import net.curre.prefcount.event.QuitActionListener;
31  import net.curre.prefcount.gui.theme.skin.PrefSkin;
32  import net.curre.prefcount.gui.aa.AAJButton;
33  import net.curre.prefcount.gui.aa.AAJPanel;
34  import net.curre.prefcount.gui.aa.AAJLabel;
35  import net.curre.prefcount.gui.menu.PrefCountMenuBar;
36  import net.curre.prefcount.service.LafThemeService;
37  import net.curre.prefcount.service.SettingsService;
38  import net.curre.prefcount.util.Utilities;
39  
40  /**
41   * Object of this class represents a player question panel
42   * wrapper - frame with navigation buttons and messages
43   * fields common to all player input dialog windows;
44   * <p/>
45   * panel objects that represent individual input steps
46   * go into the questionsPane, which has a card layout.
47   * <p/>
48   * Created date: May 6, 2007
49   *
50   * @author Yevgeny Nyden
51   */
52  public class PlayerDialogBasePanel extends JFrame {
53  
54    /** Reference to the main window object. */
55    protected MainWindow mainWindow;
56  
57    /** Reference to the player dialog frame's menu bar. */
58    protected PrefCountMenuBar menuBar;
59  
60    /** Navigation - back button. */
61    JButton backButton;
62  
63    /** Navigation - forward/next button. */
64    JButton nextButton;
65  
66    /** Reference to the (intro) message label. */
67    JLabel messageLabel;
68  
69    /** Reference to the (validation) error label. */
70    JLabel errorLabel;
71  
72    /** Reference to the questions pane. */
73    JPanel questionsPane;
74  
75    /** Number of players in the game. */
76    protected int playersNumber;
77  
78    /** Index of the current player panel. */
79    protected static int currPlayerPanel = 0;
80  
81    /**
82     * List of all navigational buttons
83     * in the order they should be traversed.
84     */
85    protected List<JButton> navigationButtons;
86  
87    /**
88     * Constructor with parameters. Note that the visibility of
89     * this frame will depend on the visibility of the main window
90     * frame. This frame will be visible if the main window frame
91     * is visible; false otherwise.
92     *
93     * @param playersNumber Number of players.
94     * @param mainWindow    Reference to the main window object.
95     */
96    public PlayerDialogBasePanel(int playersNumber, MainWindow mainWindow) {
97      super(ResourceBundle.getBundle("default").getString("pref.dialog.title"));
98  
99      Image appImg = Toolkit.getDefaultToolkit().createImage(App.class.getResource("images/PrefCount-16x16.png"));
100     super.setIconImage(appImg);
101 
102     this.playersNumber = playersNumber;
103     this.mainWindow = mainWindow;
104     navigationButtons = new ArrayList<JButton>();
105 
106     messageLabel = new AAJLabel();
107     messageLabel.setOpaque(true);
108     questionsPane = new AAJPanel();
109     questionsPane.setOpaque(true);
110     errorLabel = new AAJLabel();
111     errorLabel.setOpaque(true);
112 
113     initComponents();
114     Settings settings = SettingsService.getSettings();
115     super.setSize(settings.getDialogFrameWidth(), settings.getDialogFrameHeight());
116     super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
117     super.setLocationRelativeTo(getOwner());
118     PrefSkin skin = LafThemeService.getInstance().getCurrentSkin();
119     super.setBackground(skin.getMainBackgroundColor());
120 
121     // focusing the first panel's first input text field
122     super.addWindowListener(new WindowAdapter() {
123       public void windowOpened(WindowEvent e) {
124         getCurrentInnerPanel().focusFirstInputField();
125       }
126     });
127 
128     // adding a menu bar (only when running on Mac OS)
129     menuBar = PrefCountRegistry.getInstance().addPlayerDialogMenuBar(this);
130 
131     super.setVisible(mainWindow.isVisible());
132   }
133 
134   /**
135    * Checks if the focus should be transfered to
136    * the input fields (implemented in subclasses).
137    *
138    * @param currIndex Current index of the button on
139    *                  the navigationButtons list.
140    */
141   public void transferFocusProperly(int currIndex) {
142     for (int i = currIndex + 1; i < navigationButtons.size(); ++i) {
143       if (navigationButtons.get(i).isEnabled()) {
144         return; // let the focus transfer automatically
145       }
146     }
147     getCurrentInnerPanel().focusFirstInputField();
148   }
149 
150   /**
151    * Next or Previous button action helper.
152    *
153    * @param isNextAction True if we are switching to the next panel;
154    *                     false if previous.
155    */
156   public void nextQuestionEventHelper(boolean isNextAction) {
157     if ((isNextAction && !nextButton.isEnabled()) ||
158         (!isNextAction && !backButton.isEnabled())) {
159       return;
160     }
161     DialogInnerPanel currPanel = getCurrentInnerPanel();
162 
163     // validating the current panel if necessary
164     if (!currPanel.validateFields()) {
165       return;
166     }
167 
168     // do necessary activity, and move to the next panel
169     currPanel.doOnLeave();
170     currPlayerPanel = currPlayerPanel + (isNextAction ? 1 : -1);
171     currPanel = getCurrentInnerPanel();
172     currPanel.doOnEntry();
173 
174     // enabling/disabling navigation buttons and menu items
175     final boolean nextButtonEnabled = !currPanel.isLastPanel();
176     final boolean backButtonEnabled = !currPanel.isFirstPanel();
177     final boolean computeButtonEnabled = !nextButtonEnabled;
178     nextButton.setEnabled(nextButtonEnabled);
179     backButton.setEnabled(backButtonEnabled);
180     if (menuBar != null) {
181       menuBar.toggleNextAction(nextButtonEnabled);
182       menuBar.toggleBackAction(backButtonEnabled);
183       menuBar.toggleComputeAction(computeButtonEnabled);
184     }
185 
186     // changing the main label
187     messageLabel.setText(currPanel.getHeaderMessage());
188 
189     CardLayout clay = (CardLayout) questionsPane.getLayout();
190     if (isNextAction) {
191       clay.next(questionsPane);  // switching to the next panel
192     } else {
193       clay.previous(questionsPane); // switching to the previous panel
194     }
195     currPanel.focusFirstInputField();
196     mainWindow.repaint();      // repainting the score board
197   }
198 
199   /** This will refresh the dialog frame size. */
200   public void repaint() {
201     Settings settings = SettingsService.getSettings();
202     super.setSize(settings.getDialogFrameWidth(), settings.getDialogFrameHeight());
203   }
204 
205   /**
206    * Returns reference to the current inner panel.
207    *
208    * @return Reference to the current inner panel.
209    */
210   public DialogInnerPanel getCurrentInnerPanel() {
211     return (DialogInnerPanel) questionsPane.getComponents()[currPlayerPanel];
212   }
213 
214   /**
215    * Sets/hides the error label.
216    *
217    * @param message Error message or null if the error
218    *                label should be hidden.
219    */
220   void toggleErrorField(String message) {
221     if (message == null) {
222       errorLabel.setText("");
223       errorLabel.setVisible(false);
224     } else {
225       errorLabel.setText(message);
226       errorLabel.setVisible(true);
227     }
228   }
229 
230   /**
231    * Sets players names in the GameResultBean bean.
232    *
233    * @param playersNames List with player names.
234    */
235   void setPlayersNames(List<String> playersNames) {
236     List<PlayerStatistics> stats = mainWindow.playerResults.getPlayerStats();
237     for (int i = 0; i < stats.size(); ++i) {
238       PlayerStatistics stat = stats.get(i);
239       stat.setPlayerName(playersNames.get(i));
240     }
241   }
242 
243   /** Sets the focus on the first focusable navigation button. */
244   protected void focusFirstNavigationalButton() {
245     for (JButton button : navigationButtons) {
246       if (button.isEnabled()) {
247         button.requestFocus();
248         return;
249       }
250     }
251     getCurrentInnerPanel().focusFirstInputField();
252   }
253 
254   /** Private methods ********************** */
255 
256   /** Initializes the components. */
257   private void initComponents() {
258     JPanel navigationPanel = new JPanel();
259     navigationPanel.setOpaque(true);
260     JPanel mainContentPanel = new JPanel();
261     mainContentPanel.setOpaque(true);
262     JPanel messagePanel = new JPanel();
263     messagePanel.setOpaque(true);
264     JPanel errorPanel = new JPanel();
265     errorPanel.setOpaque(true);
266 
267     // adding Ctrl-Q - quit shortcut to the panel if not on Mac OS;
268     // Mac OS will have a menu for every frame (with a Quit shortcut)
269     if (!Utilities.isMacOs()) {
270       mainContentPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
271           .put(KeyStroke.getKeyStroke("control Q"), "exitAppAction");
272       mainContentPanel.getActionMap().put("exitAppAction", new QuitActionListener());
273     }
274     ResourceBundle bundle = ResourceBundle.getBundle("default");
275 
276     //======== this ========
277     Container contentPaneOut = getContentPane();
278     contentPaneOut.setLayout(new BorderLayout(5, 5));
279 
280     //======== navigationPanel ========
281     {
282       navigationPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 10));
283 
284       //---- backButton ----
285       backButton = createButtonHelper(bundle.getString("pref.dialog.backButton.label"),
286                                       bundle.getString("pref.dialog.backButton.shortcut").charAt(0),
287                                       false, false);
288       navigationPanel.add(backButton);
289       navigationPanel.add(new JPanel(null));
290 
291       //---- nextButton ----
292       nextButton = createButtonHelper(bundle.getString("pref.dialog.nextButton.label"),
293                                       bundle.getString("pref.dialog.nextButton.shortcut").charAt(0),
294                                       true, true);
295       navigationPanel.add(nextButton);
296     }
297     contentPaneOut.add(navigationPanel, BorderLayout.NORTH);
298 
299     //======== mainContentPanel ========
300     {
301       mainContentPanel.setLayout(new BorderLayout());
302 
303       //======== messagePanel ========
304       {
305         messagePanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
306 
307         //---- messageLabel ----
308         messageLabel.setText(bundle.getString("pref.dialog.message.default"));
309         messagePanel.add(messageLabel);
310       }
311       mainContentPanel.add(messagePanel, BorderLayout.NORTH);
312 
313       JPanel contentPaneWrapper = new JPanel();
314       //======== contentPane ========
315       {
316         contentPaneWrapper.setLayout(new FlowLayout());
317         JPanel playerNamesPanel = new PlayersNamesPanel(this, playersNumber);
318         messageLabel.setText(((DialogInnerPanel) playerNamesPanel).getHeaderMessage());
319         messageLabel.setOpaque(true);
320         questionsPane.setLayout(new CardLayout());
321         questionsPane.add(playerNamesPanel, "Player Names");
322         questionsPane.add(new PlayerDataPanel(this, playersNumber, 0), "Player 0");
323         questionsPane.add(new PlayerDataPanel(this, playersNumber, 1), "Player 1");
324         questionsPane.add(new PlayerDataPanel(this, playersNumber, 2), "Player 2");
325         if (playersNumber == 4) {
326           questionsPane.add(new PlayerDataPanel(this, playersNumber, 3), "Player 3");
327         }
328 
329         LastInputPanel lastInputPanel = new LastInputPanel(this);
330         PrefCountRegistry.getInstance().setLastInputPanel(lastInputPanel);
331         questionsPane.add(lastInputPanel, "Last Input");
332         contentPaneWrapper.add(questionsPane);
333       }
334       mainContentPanel.add(contentPaneWrapper, BorderLayout.CENTER);
335     }
336     contentPaneOut.add(mainContentPanel, BorderLayout.CENTER);
337 
338     //======== errorPanel ========
339     {
340       errorPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 10));
341 
342       //---- errorLabel ----
343       errorLabel.setText(bundle.getString("pref.dialog.errorLabel.default"));
344       errorLabel.setForeground(Color.RED);
345       errorLabel.setVisible(false);
346       errorPanel.add(errorLabel);
347     }
348     contentPaneOut.add(errorPanel, BorderLayout.SOUTH);
349     setLocationRelativeTo(getOwner());
350   }
351 
352   /**
353    * Helper method to create a navigational button.
354    * This method also adds proper action, key and
355    * focus listeners to the newly created button.
356    *
357    * @param label        Button label.
358    * @param mnemonicCode Mnemonic char for this button.
359    * @param isNextButton True if this is a next button; false otherwise.
360    * @param isEnabled    True if this button is enabled; false otherwise.
361    * @return A newly created button.
362    */
363   private JButton createButtonHelper(String label, char mnemonicCode,
364                                      boolean isNextButton, boolean isEnabled) {
365     JButton button = new AAJButton();
366     navigationButtons.add(button);
367     button.setEnabled(isEnabled);
368     button.setText(Utilities.underlineLetter(label, 0));
369     DialogButtonNavigationListener dialogListener =
370         new DialogButtonNavigationListener(isNextButton, this);
371     button.addFocusListener(dialogListener);
372     button.addActionListener(dialogListener);
373     button.addKeyListener(dialogListener);
374     button.setMnemonic(mnemonicCode);
375 
376     // adding the button shortcut (only if we are not running on Mac OS);
377     // we assume here, that for Mac OS, all button shortcuts are added to the frame's menu bar
378     if (!Utilities.isMacOs()) {
379       InputMap map = questionsPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
380       String actionName = "button" + mnemonicCode + "Action";
381       map.put(KeyStroke.getKeyStroke("control " + mnemonicCode), actionName);
382       questionsPane.getActionMap().put(actionName, dialogListener);
383     }
384 
385     return button;
386   }
387 
388 }