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 version 2 as 
4    * published by the Free Software Foundation;
5    */
6   
7   package net.curre.prefcount.gui.menu;
8   
9   import javax.swing.AbstractButton;
10  import javax.swing.ButtonGroup;
11  import javax.swing.InputMap;
12  import javax.swing.JButton;
13  import javax.swing.JComponent;
14  import javax.swing.JMenuItem;
15  import javax.swing.JPanel;
16  import javax.swing.JRadioButton;
17  import javax.swing.JRadioButtonMenuItem;
18  import javax.swing.KeyStroke;
19  import javax.swing.AbstractAction;
20  import java.awt.CheckboxMenuItem;
21  import java.awt.MenuItem;
22  import java.awt.MenuShortcut;
23  import java.awt.event.ActionEvent;
24  import java.awt.event.ActionListener;
25  import java.awt.event.FocusListener;
26  import java.awt.event.ItemEvent;
27  import java.awt.event.ItemListener;
28  import java.awt.event.KeyEvent;
29  import java.awt.event.KeyListener;
30  import java.util.HashMap;
31  import java.util.Map;
32  
33  import net.curre.prefcount.event.MainController;
34  import net.curre.prefcount.gui.aa.AAJButton;
35  import net.curre.prefcount.gui.type.WindowComponent;
36  import static net.curre.prefcount.gui.type.WindowComponent.DIALOG_FORWARD2;
37  import net.curre.prefcount.util.LocaleExt;
38  import net.curre.prefcount.util.Utilities;
39  
40  /**
41   * Object of this class represents menu items bean to
42   * coordinate action events among menu bar menu items and
43   * option panel radio buttons.
44   * <p/>
45   * Created date: Apr 9, 2008
46   *
47   * @author Yevgeny Nyden
48   */
49  public class MenuItemsBean {
50  
51    /** Action listener to listen on all item action events. */
52    private ActionListener actionListener;
53  
54    /** Map with radio button menu items. */
55    private Map<WindowComponent, JRadioButtonMenuItem> menuBarRadioItems;
56  
57    /** Map with awt check box menu items. */
58    private Map<WindowComponent, CheckboxMenuItem> menuBarCheckBoxItems;
59  
60    /** Menu bar menu items map. */
61    private Map<WindowComponent, JMenuItem> menuBarMenuItems;
62  
63    /** Awt menu bar menu items map. */
64    private Map<WindowComponent, MenuItem> menuAwtBarMenuItems;
65  
66    /** Menu bar groups map. */
67    private Map<String, ButtonGroup> menuBarGroups;
68  
69    /** Awt menu bar groups map. */
70    private Map<String, AwtCheckboxMenuGroup> menuBarAwtGroups;
71  
72    /** Radio buttons map. */
73    private Map<WindowComponent, JRadioButton> radioButtons;
74  
75    /** JButtons map. */
76    private Map<WindowComponent, JButton> jButtons;
77  
78    /** Radio buttons groups map. */
79    private Map<String, ButtonGroup> radioButtonGroups;
80  
81    /** Constructs a new menu items bean object. */
82    public MenuItemsBean() {
83      this.menuBarRadioItems = new HashMap<WindowComponent, JRadioButtonMenuItem>();
84      this.menuBarMenuItems = new HashMap<WindowComponent, JMenuItem>();
85      this.menuAwtBarMenuItems = new HashMap<WindowComponent, MenuItem>();
86      this.menuBarCheckBoxItems = new HashMap<WindowComponent, CheckboxMenuItem>();
87      this.menuBarGroups = new HashMap<String, ButtonGroup>();
88      this.menuBarAwtGroups = new HashMap<String, AwtCheckboxMenuGroup>();
89      this.radioButtons = new HashMap<WindowComponent, JRadioButton>();
90      this.radioButtonGroups = new HashMap<String, ButtonGroup>();
91      this.jButtons = new HashMap<WindowComponent, JButton>();
92    }
93  
94    /**
95     * Setter for the main items action listener. Note that when this
96     * action triggers, the event's source will be set to the corresponding
97     * item's <code>WindowComponent</code> enum.
98     *
99     * @param actionListener action listener to set.
100    */
101   public void setActionListener(ActionListener actionListener) {
102     this.actionListener = actionListener;
103   }
104 
105   /**
106    * Fetches a <code>JRadioButtonMenuItem</code> menu item
107    * for the given item enumeration. The item will be created
108    * if it does not exist.
109    *
110    * @param itemEnum item enumeration that represents the required item.
111    * @return radio button menu item that corresponds to the item enum.
112    */
113   public JRadioButtonMenuItem getJRadioButtonMenuItem(WindowComponent itemEnum) {
114     JRadioButtonMenuItem item = this.menuBarRadioItems.get(itemEnum);
115     if (item == null) {
116       String text = LocaleExt.getString(itemEnum.getTextKey());
117       item = new JRadioButtonMenuItem(text);
118       LocaleExt.registerComponent(item, itemEnum);
119       item.addActionListener(new ItemActionListener(itemEnum));
120       this.menuBarRadioItems.put(itemEnum, item);
121 
122 /*
123       if (itemEnum.shortcutKey != null) {
124         item.setToolTipText(LocaleExt.getString(itemEnum.shortcutKey));
125         ToolTipManager.sharedInstance().registerComponent(item);
126       }
127 */
128 
129       if (itemEnum.groupKey != null) {
130         ButtonGroup group = this.menuBarGroups.get(itemEnum.groupKey);
131         if (group == null) {
132           group = new ButtonGroup();
133           this.menuBarGroups.put(itemEnum.groupKey, group);
134         }
135         group.add(item);
136       }
137 
138       if (itemEnum.getShortcutKey() != null) {
139         item.setAccelerator(KeyStroke.getKeyStroke(
140             LocaleExt.getString(itemEnum.getShortcutKey()).charAt(0), ActionEvent.CTRL_MASK));
141       }
142     }
143     return item;
144   }
145 
146   /**
147    * Fetches a <code>CheckboxMenuItem</code> menu item
148    * for the given item enumeration. The item will be created
149    * if it does not exist.
150    *
151    * @param itemEnum item enumeration that represents the required item.
152    * @return awt check box button menu item that corresponds to the item enum.
153    */
154   public CheckboxMenuItem getRadioButtonMenuItem(WindowComponent itemEnum) {
155 
156     CheckboxMenuItem item = this.menuBarCheckBoxItems.get(itemEnum);
157     if (item == null) {
158       String text = LocaleExt.getString(itemEnum.getTextKey());
159       item = new CheckboxMenuItem(text);
160       LocaleExt.registerComponent(item, itemEnum);
161       item.addItemListener(new ItemActionListener(itemEnum));
162       this.menuBarCheckBoxItems.put(itemEnum, item);
163 
164 /*
165       if (itemEnum.shortcutKey != null) {
166         item.setToolTipText(LocaleExt.getString(itemEnum.shortcutKey));
167         ToolTipManager.sharedInstance().registerComponent(item);
168       }
169 */
170 
171       if (itemEnum.groupKey != null) {
172         AwtCheckboxMenuGroup group = this.menuBarAwtGroups.get(itemEnum.groupKey);
173         if (group == null) {
174           group = new AwtCheckboxMenuGroup();
175           this.menuBarAwtGroups.put(itemEnum.groupKey, group);
176         }
177         group.addItemToGroup(item);
178       }
179 
180       if (itemEnum.getShortcutKey() != null) {
181         item.setShortcut(new MenuShortcut(LocaleExt.getString(itemEnum.getShortcutKey()).charAt(0), false));
182       }
183     }
184     return item;
185   }
186 
187   /**
188    * Fetches a <code>JRadioButton</code> radio button
189    * for the given item enumeration. The item will be created
190    * if it does not exist.
191    *
192    * @param itemEnum item enumeration that represents the required item.
193    * @return radio button that corresponds to the item enum.
194    */
195   public JRadioButton getJRadioButton(WindowComponent itemEnum) {
196     JRadioButton button = this.radioButtons.get(itemEnum);
197     if (button == null) {
198       button = new JRadioButton(Utilities.generateButtonText(itemEnum));
199       LocaleExt.registerComponent(button, itemEnum);
200       button.addActionListener(new ItemActionListener(itemEnum));
201       this.radioButtons.put(itemEnum, button);
202 
203 
204       if (itemEnum.tooltipKey != null) {
205         button.setToolTipText(LocaleExt.getString(itemEnum.tooltipKey));
206       }
207 
208       if (itemEnum.groupKey != null) {
209         ButtonGroup group = this.radioButtonGroups.get(itemEnum.groupKey);
210         if (group == null) {
211           group = new ButtonGroup();
212           this.radioButtonGroups.put(itemEnum.groupKey, group);
213         }
214         group.add(button);
215       }
216     }
217 
218     return button;
219   }
220 
221   /**
222    * Fetches a <code>JButton</code> object for the given
223    * item enumeration. The item will be created if it does
224    * not exist.
225    *
226    * @param itemEnum item enumeration that represents the required item.
227    * @param pane     pass this object to add a shortcut to it (of present on the item's enum).
228    * @return button that corresponds to the item enum.
229    */
230   public JButton getJButton(WindowComponent itemEnum, JPanel pane) {
231     JButton button = this.jButtons.get(itemEnum);
232     if (button == null) {
233       button = new AAJButton(Utilities.generateButtonText(itemEnum));
234       ItemActionListener listener = new ItemActionListener(itemEnum);
235       button.addActionListener(listener);
236 
237       // win key listener produces "double click", so don't add it
238       if (Utilities.getPlatformType() != Utilities.PlatformType.WINDOWS) {
239         button.addKeyListener(listener);
240       }
241         
242       final String shortcutKey = itemEnum.getShortcutKey();
243       if (shortcutKey != null) {
244         char mnemonicCode = LocaleExt.getString(itemEnum.getShortcutKey()).charAt(0);
245         button.setMnemonic(mnemonicCode);
246 
247         // adding shortcuts if the pane object is present
248         if (pane != null) {
249           InputMap map = pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
250           String actionKey = LocaleExt.getString(shortcutKey);
251           map.put(KeyStroke.getKeyStroke("control " + actionKey), shortcutKey);
252           pane.getActionMap().put(shortcutKey, MainController.getActionForComponent(itemEnum));
253           LocaleExt.registerShortcuts(map, shortcutKey);
254         }
255       }
256 
257       this.jButtons.put(itemEnum, button);
258     }
259 
260     return button;
261   }
262 
263   /**
264    * Creates a JButton for the choose player dialog.
265    * This is a special case because the choose player window is
266    * created and disposed every time (we don't hide it).
267    *
268    * @param pane panel object to add a shortcut to it (required).
269    * @return created button object. 
270    */
271   public JButton createJButtonForChoosePlayerDialog(JPanel pane) {
272     WindowComponent itemEnum = DIALOG_FORWARD2;
273     JButton button = new AAJButton(Utilities.generateButtonText(itemEnum));
274     ItemActionListener listener = new ItemActionListener(itemEnum);
275     button.addActionListener(listener);
276 
277     // win key listener produces "double click", so don't add it
278     if (Utilities.getPlatformType() != Utilities.PlatformType.WINDOWS) {
279       button.addKeyListener(listener);
280     }
281 
282     final String shortcutKey = itemEnum.getShortcutKey();
283     if (shortcutKey != null) {
284       char mnemonicCode = LocaleExt.getString(itemEnum.getShortcutKey()).charAt(0);
285       final String shortcut = (Utilities.isMacOs() ? "meta " : "control ") + mnemonicCode;
286       pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
287           .put(KeyStroke.getKeyStroke(shortcut), "moveForward");
288       pane.getActionMap().put("moveForward", listener);
289     }
290 
291     return button;
292   }
293 
294   /**
295    * Fetches a <code>JMenuItem</code> menu item
296    * for the given item enumeration. The item will be created
297    * if it does not exist.
298    *
299    * @param itemEnum item enumeration that represents the required item.
300    * @return menu item that corresponds to the item enum.
301    */
302   public JMenuItem getJMenuItem(WindowComponent itemEnum) {
303     JMenuItem menuItem = this.menuBarMenuItems.get(itemEnum);
304     if (menuItem == null) {
305       String text = LocaleExt.getString(itemEnum.getTextKey());
306       menuItem = new JMenuItem(text);
307       LocaleExt.registerComponent(menuItem, itemEnum);
308       if (itemEnum.getShortcutKey() != null) {
309         menuItem.setAccelerator(KeyStroke.getKeyStroke(
310             LocaleExt.getString(itemEnum.getShortcutKey()).charAt(0), ActionEvent.CTRL_MASK));
311       }
312       menuItem.addActionListener(new ItemActionListener(itemEnum));
313       this.menuBarMenuItems.put(itemEnum, menuItem);
314     }
315     return menuItem;
316   }
317 
318   /**
319    * Fetches a <code>MenuItem</code> menu item
320    * for the given item enumeration. The item will be created
321    * if it does not exist.
322    *
323    * @param itemEnum item enumeration that represents the required item.
324    * @return menu item that corresponds to the item enum.
325    */
326   public MenuItem getMenuItem(WindowComponent itemEnum) {
327     MenuItem menuItem = this.menuAwtBarMenuItems.get(itemEnum);
328     if (menuItem == null) {
329       menuItem = new MenuItem(LocaleExt.getString(itemEnum.getTextKey()));
330       if (itemEnum.getShortcutKey() != null) {
331         menuItem.setShortcut(new MenuShortcut(LocaleExt.getString(itemEnum.getShortcutKey()).charAt(0), false));
332       }
333       LocaleExt.registerComponent(menuItem, itemEnum);
334       menuItem.addActionListener(new ItemActionListener(itemEnum));
335       this.menuAwtBarMenuItems.put(itemEnum, menuItem);
336     }
337     return menuItem;
338   }
339 
340   /**
341    * Adds listener to provided items.
342    *
343    * @param listener  action listener to add to the provided items.
344    * @param itemEnums item enumeration array.
345    */
346   public void addListener(Object listener, WindowComponent... itemEnums) {
347     for (WindowComponent item : itemEnums) {
348       addListenerHelper(this.radioButtons.get(item), listener);
349       addListenerHelper(this.jButtons.get(item), listener);
350       addListenerHelper(this.menuBarRadioItems.get(item), listener);
351       addListenerHelper(this.menuBarMenuItems.get(item), listener);
352     }
353   }
354 
355   /**
356    * Sets given radio button item selected status.
357    *
358    * @param itemEnum   item enumeration that represents the required radio item.
359    * @param isSelected true if the item should be selected; false otherwise.
360    */
361   public void setSelected(WindowComponent itemEnum, boolean isSelected) {
362     JRadioButtonMenuItem menuItem = MenuItemsBean.this.menuBarRadioItems.get(itemEnum);
363     if (menuItem != null) {
364       menuItem.setSelected(isSelected);
365     }
366     JRadioButton button = MenuItemsBean.this.radioButtons.get(itemEnum);
367     if (button != null) {
368       button.setSelected(isSelected);
369     }
370 
371     // selecting an awt check box menu item if present
372     CheckboxMenuItem checkItem = MenuItemsBean.this.menuBarCheckBoxItems.get(itemEnum);
373     if (checkItem != null) {
374       checkItem.dispatchEvent(new ActionEvent(itemEnum, ActionEvent.ACTION_PERFORMED, null));
375     }
376   }
377 
378   /** Private methods ***********************/
379 
380   /**
381    * Helper method to add a listener to a component.
382    * Note that this method is null safe.
383    *
384    * @param component component to add a listener to (must be an AbstractButton).
385    * @param listener  listener (supported are: ActionListener,
386    *                  FocusListener, and KeyListener).
387    */
388   private void addListenerHelper(AbstractButton component, Object listener) {
389     if (component != null && listener != null) {
390       if (listener instanceof ActionListener) {
391         component.addActionListener((ActionListener) listener);
392 
393       } else if (listener instanceof FocusListener) {
394         component.addFocusListener((FocusListener) listener);
395 
396       } else if (listener instanceof KeyListener) {
397         component.addKeyListener((KeyListener) listener);
398 
399       } else {
400         throw new IllegalArgumentException("Illegal listener class: " + listener.getClass().getName());
401       }
402     }
403   }
404 
405   /**
406    * Item's action listener that assists with selecting
407    * all items that refer to the same option (item enum) and
408    * performing an action on the bean's action listener
409    * (if it's set).
410    */
411   private class ItemActionListener extends AbstractAction implements ActionListener, ItemListener, KeyListener {
412 
413     /** This enum represent an item this listener belongs to. */
414     private WindowComponent itemEnum;
415 
416     /**
417      * Constructs a new <code>ItemActionListener</code> object.
418      *
419      * @param itemEnum item enum to set.
420      */
421     private ItemActionListener(WindowComponent itemEnum) {
422       this.itemEnum = itemEnum;
423     }
424 
425     /** {@inheritDoc} */
426     public void actionPerformed(ActionEvent event) {
427       doAction(event);
428     }
429 
430     /** {@inheritDoc} */
431     public void itemStateChanged(ItemEvent event) {
432       ActionEvent aEvent = new ActionEvent(this.itemEnum, ActionEvent.ACTION_PERFORMED, this.itemEnum.getTextKey());
433       doAction(aEvent);
434     }
435 
436     /** Does nothing. */
437     public void keyTyped(KeyEvent event) {
438     }
439 
440     /** Does nothing. */
441     public void keyPressed(KeyEvent event) {
442     }
443 
444     /** {@inheritDoc} */
445     public void keyReleased(KeyEvent event) {
446       if (KeyEvent.VK_ENTER == event.getKeyCode()) {
447         ActionEvent aEvent = new ActionEvent(this.itemEnum, ActionEvent.ACTION_PERFORMED, this.itemEnum.getTextKey());
448         doAction(aEvent);
449       }
450     }
451 
452     /**
453      * Helper method to perform action.
454      *
455      * @param event event for this action.
456      */
457     private void doAction(ActionEvent event) {
458       MenuItemsBean.this.setSelected(this.itemEnum, true);
459       if (MenuItemsBean.this.actionListener != null) {
460         event.setSource(this.itemEnum);
461         MenuItemsBean.this.actionListener.actionPerformed(event);
462       }
463     }
464 
465   }
466 
467 }