View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.ui;
39  
40  import static javax.swing.Action.ACCELERATOR_KEY;
41  import static javax.swing.Action.ACTION_COMMAND_KEY;
42  import static javax.swing.Action.LARGE_ICON_KEY;
43  import static javax.swing.Action.LONG_DESCRIPTION;
44  import static javax.swing.Action.MNEMONIC_KEY;
45  import static javax.swing.Action.NAME;
46  import static javax.swing.Action.SHORT_DESCRIPTION;
47  import static javax.swing.Action.SMALL_ICON;
48  
49  import ffx.ui.properties.FFXLocale;
50  import java.awt.Insets;
51  import java.awt.event.ActionEvent;
52  import java.awt.event.ActionListener;
53  import java.awt.event.KeyEvent;
54  import java.io.Serial;
55  import javax.swing.AbstractAction;
56  import javax.swing.Action;
57  import javax.swing.ButtonGroup;
58  import javax.swing.ImageIcon;
59  import javax.swing.JButton;
60  import javax.swing.JCheckBoxMenuItem;
61  import javax.swing.JMenu;
62  import javax.swing.JMenuBar;
63  import javax.swing.JMenuItem;
64  import javax.swing.JRadioButtonMenuItem;
65  import javax.swing.JToolBar;
66  import javax.swing.KeyStroke;
67  import org.apache.commons.lang3.SystemUtils;
68  
69  /**
70   * The MainMenu class creates the Force Field X Menu Bar
71   *
72   * @author Michael J. Schnieders
73   */
74  public class MainMenu extends JMenuBar {
75  
76    @Serial
77    private static final long serialVersionUID = 1L;
78  
79    /**
80     * Note that the getMenuShortcutKeyMask() is deprecated in JDK 10 and replaced with
81     * getMenuShortcutKeyMaskEx(). However, this later method is not present in JDK 8/9.
82     */
83    @SuppressWarnings("deprecation")
84    private static final int keyMask = java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
85  
86    // Locale and ClassLoader
87    private final FFXLocale locale;
88    private final ClassLoader loader;
89    // Toolbar
90    private final JToolBar toolBar;
91    private final ImageIcon blankIcon;
92  
93    // Selection Menu
94    private final JCheckBoxMenuItem highlightCBMI;
95    private final JCheckBoxMenuItem labelResiduesMI;
96    private final JCheckBoxMenuItem labelAtomsMI;
97    // Options Menu
98    private final JRadioButtonMenuItem activeRBMI;
99    private final JRadioButtonMenuItem mouseRBMI;
100   // Picking Menu
101   private final JCheckBoxMenuItem pickingCBMI;
102   private final JRadioButtonMenuItem atomRBMI;
103   private final JRadioButtonMenuItem bondRBMI;
104   private final JRadioButtonMenuItem angleRBMI;
105   private final JRadioButtonMenuItem dihedralRBMI;
106   private final JRadioButtonMenuItem residueRBMI;
107   private final JRadioButtonMenuItem polymerRBMI;
108   private final JRadioButtonMenuItem moleculeRBMI;
109   private final JRadioButtonMenuItem systemRBMI;
110   private final JRadioButtonMenuItem measureDistanceRBMI;
111   private final JRadioButtonMenuItem measureAngleRBMI;
112   private final JRadioButtonMenuItem measureDihedralRBMI;
113   // Simulation Menu
114   private JMenuItem localMI;
115   private JMenuItem remoteMI;
116   private JMenuItem releaseMI;
117   // Window Menu
118   private final JCheckBoxMenuItem systemsCBMI;
119   private final JCheckBoxMenuItem toolBarCBMI;
120   private final JCheckBoxMenuItem globalAxisCBMI;
121 
122   /*
123    * Constructor
124    * @param f Main application controller.
125    */
126 
127   /**
128    * Constructor for MainMenu.
129    *
130    * @param f a {@link ffx.ui.MainPanel} object.
131    */
132   public MainMenu(MainPanel f) {
133 
134     // Create the Tool Bar
135     toolBar = new JToolBar("Toolbar", JToolBar.HORIZONTAL);
136     toolBar.setBorderPainted(true);
137     toolBar.setRollover(true);
138     JButton temp = new JButton();
139     Insets insets = temp.getInsets();
140     insets.set(2, 2, 2, 2);
141 
142     // Controller References
143     GraphicsCanvas graphics = f.getGraphics3D();
144     locale = f.getFFXLocale();
145     loader = getClass().getClassLoader();
146     String icons = "ffx/ui/icons/";
147     blankIcon = new ImageIcon(loader.getResource(icons + "blank.gif"));
148 
149     String value = System.getProperty("structures", "false").trim();
150     // Structure Menu
151     boolean includeStructureMenu;
152     try {
153       includeStructureMenu = Boolean.parseBoolean(value);
154     } catch (Exception e) {
155       includeStructureMenu = false;
156     }
157 
158     // Main Menubar
159     JMenu fileMenu = addMenu("File", 'F');
160     JMenu selectionMenu = addMenu("Selection", 'E');
161     JMenu structureMenu = null;
162     if (includeStructureMenu) {
163       structureMenu = addMenu("Structure", 'S');
164     }
165     JMenu displayMenu = addMenu("Display", 'D');
166     JMenu colorMenu = addMenu("Color", 'C');
167     JMenu optionsMenu = addMenu("Options", 'O');
168     JMenu pickingMenu = addMenu("Picking", 'P');
169     JMenu trajectoryMenu = addMenu("Trajectory", 'T');
170     JMenu exportMenu = addMenu("Export", 'X');
171     JMenu windowMenu = addMenu("Window", 'W');
172     JMenu helpMenu = addMenu("Help", 'H');
173 
174     // File Menu - Events Handled by the MainPanel Class.
175     addMenuItem(fileMenu, icons + "folder_page", "Open", 'O', KeyEvent.VK_O, f);
176     addMenuItem(fileMenu, icons + "disk", "SaveAs", 'S', KeyEvent.VK_S, f);
177     addMenuItem(fileMenu, icons + "cancel", "Close", 'C', -1, f);
178     addMenuItem(fileMenu, "BLANK", "CloseAll", 'A', -1, f);
179     fileMenu.addSeparator();
180     addMenuItem(fileMenu, icons + "drive_web", "DownloadFromPDB", 'D', KeyEvent.VK_D, f);
181     fileMenu.addSeparator();
182     addMenuItem(fileMenu, "BLANK", "ChooseKeyFile", 'R', -1, f);
183     addMenuItem(fileMenu, "BLANK", "ChooseLogFile", 'I', -1, f);
184     addMenuItem(fileMenu, "BLANK", "LoadRestartData", 'R', -1, f);
185     if (!SystemUtils.IS_OS_MAC_OSX) {
186       fileMenu.addSeparator();
187       addMenuItem(fileMenu, "BLANK", "Exit", 'E', KeyEvent.VK_Q, f);
188     }
189     toolBar.addSeparator();
190 
191     // Selection Menu - Events Handled by the MainPanel and GraphicsCanvas.
192     addMenuItem(selectionMenu, icons + "add", "SelectAll", 'A', KeyEvent.VK_A, f);
193     addMenuItem(selectionMenu, "BLANK", "RestrictToSelections", 'R', -1, graphics);
194     addMenuItem(selectionMenu, icons + "arrow_merge", "MergeSelections", 'M', -1, f);
195     selectionMenu.addSeparator();
196     highlightCBMI =
197         addCBMenuItem(
198             selectionMenu, icons + "asterisk_yellow", "HighlightSelections", 'H', KeyEvent.VK_H, f);
199     addMenuItem(selectionMenu, "BLANK", "SetSelectionColor", 'S', -1, graphics);
200     selectionMenu.addSeparator();
201     labelAtomsMI = addCBMenuItem(selectionMenu, "BLANK", "LabelSelectedAtoms", 'O', -1, graphics);
202     labelResiduesMI =
203         addCBMenuItem(selectionMenu, "BLANK", "LabelSelectedResidues", 'R', -1, graphics);
204     addMenuItem(selectionMenu, "BLANK", "SetLabelFontSize", 'Z', -1, graphics);
205     addMenuItem(selectionMenu, "BLANK", "SetLabelFontColor", 'C', -1, graphics);
206     highlightCBMI.setSelected(false);
207     labelAtomsMI.setSelected(false);
208     labelResiduesMI.setSelected(false);
209     toolBar.addSeparator();
210 
211     // Structure Menu - Events Handled by the MainPanel.
212     if (includeStructureMenu) {
213       // Locate a jar file that has PDB Structures.
214       String file = "ffx/xray/structures/1N7S.pdb";
215       addMenuItem(structureMenu, "BLANK", file, '.', -1, f);
216     }
217 
218     // Display Menu - Events handled by the GraphicsCanvas.
219     addMenuItem(displayMenu, "BLANK", "Wireframe", 'W', -1, graphics);
220     addMenuItem(displayMenu, "BLANK", "Tube", 'T', -1, graphics);
221     addMenuItem(displayMenu, "BLANK", "Spacefill", 'S', -1, graphics);
222     addMenuItem(displayMenu, "BLANK", "BallAndStick", 'B', -1, graphics);
223     addMenuItem(displayMenu, "BLANK", "Invisible", 'I', -1, graphics);
224     addMenuItem(displayMenu, "BLANK", "RMIN", 'R', -1, graphics);
225     displayMenu.addSeparator();
226     addMenuItem(displayMenu, "BLANK", "ShowHydrogens", 'H', -1, graphics);
227     addMenuItem(displayMenu, "BLANK", "HideHydrogens", 'Y', -1, graphics);
228     displayMenu.addSeparator();
229     addMenuItem(displayMenu, "BLANK", "Fill", 'F', -1, graphics);
230     addMenuItem(displayMenu, "BLANK", "Points", 'P', -1, graphics);
231     addMenuItem(displayMenu, "BLANK", "Lines", 'I', -1, graphics);
232     displayMenu.addSeparator();
233     addMenuItem(displayMenu, "BLANK", "Preferences", 'P', -1, graphics);
234 
235     // Color Menu - Events handled by the GraphicsCanvas.
236     addMenuItem(colorMenu, "BLANK", "Monochrome", 'M', -1, graphics);
237     addMenuItem(colorMenu, "BLANK", "CPK", 'C', -1, graphics);
238     addMenuItem(colorMenu, "BLANK", "Residue", 'R', -1, graphics);
239     addMenuItem(colorMenu, "BLANK", "Structure", 'S', -1, graphics);
240     addMenuItem(colorMenu, "BLANK", "Polymer", 'M', -1, graphics);
241     addMenuItem(colorMenu, "BLANK", "PartialCharge", 'P', -1, graphics);
242     addMenuItem(colorMenu, "BLANK", "UserColor", 'U', -1, graphics);
243     colorMenu.addSeparator();
244     addMenuItem(colorMenu, "BLANK", "ApplyUserColor", 'A', -1, graphics);
245     addMenuItem(colorMenu, "BLANK", "SetUserColor", 'C', -1, graphics);
246 
247     // Options Menu - Events handled by the GraphicsCanvas.
248     ButtonGroup dragModeButtonGroup = new ButtonGroup();
249     activeRBMI =
250         addBGMI(
251             dragModeButtonGroup,
252             optionsMenu,
253             "BLANK",
254             "ActiveSystem",
255             'A',
256             KeyEvent.VK_A,
257             graphics);
258     mouseRBMI =
259         addBGMI(
260             dragModeButtonGroup,
261             optionsMenu,
262             "BLANK",
263             "SystemBelowMouse",
264             'S',
265             KeyEvent.VK_M,
266             graphics);
267     activeRBMI.setSelected(true);
268     optionsMenu.addSeparator();
269     JMenu leftMouseMenu = addSubMenu(optionsMenu, "LeftMouseButton", 'M');
270     ButtonGroup leftMouseButtonGroup = new ButtonGroup();
271     JRadioButtonMenuItem rotateRBMI =
272         addBGMI(
273             leftMouseButtonGroup, leftMouseMenu, "BLANK", "Rotate", 'R', KeyEvent.VK_R, graphics);
274     addBGMI(
275         leftMouseButtonGroup, leftMouseMenu, "BLANK", "Translate", 'T', KeyEvent.VK_T, graphics);
276     addBGMI(leftMouseButtonGroup, leftMouseMenu, "BLANK", "Zoom", 'Z', KeyEvent.VK_Z, graphics);
277     rotateRBMI.setSelected(true);
278     optionsMenu.addSeparator();
279     addMenuItem(optionsMenu, "BLANK", "RotateAboutCenter", 'C', KeyEvent.VK_C, graphics);
280     addMenuItem(optionsMenu, "BLANK", "RotateAboutPick", 'P', KeyEvent.VK_P, graphics);
281     addMenuItem(optionsMenu, "BLANK", "ResetRotation", 'R', -1, graphics);
282     addMenuItem(optionsMenu, "BLANK", "ResetTranslation", 'T', -1, graphics);
283     addMenuItem(
284         optionsMenu, icons + "arrow_refresh", "ResetRotationAndTranslation", 'E', -1, graphics);
285     optionsMenu.addSeparator();
286     addMenuItem(optionsMenu, icons + "magnifier_zoom_in", "ZoomIn", 'I', -1, graphics);
287     addMenuItem(optionsMenu, icons + "magnifier_zoom_out", "ZoomOut", 'O', -1, graphics);
288     addMenuItem(optionsMenu, "BLANK", "ResetGlobalZoom", 'Z', -1, graphics);
289     addMenuItem(optionsMenu, "BLANK", "ResetGlobalRotation", 'N', -1, graphics);
290     addMenuItem(optionsMenu, "BLANK", "ResetGlobalTranslation", 'O', -1, graphics);
291     addMenuItem(optionsMenu, icons + "house", "ResetGlobalView", 'V', -1, graphics);
292     optionsMenu.addSeparator();
293     addMenuItem(optionsMenu, "BLANK", "SetBackgroundColor", 'B', -1, graphics);
294     toolBar.addSeparator();
295 
296     // Picking Menu - Events handled by the GraphicsCanvas.
297     ButtonGroup levelBG = new ButtonGroup();
298     pickingCBMI =
299         addCBMenuItem(pickingMenu, icons + "wand", "GraphicsPicking", 'G', KeyEvent.VK_0, graphics);
300     pickingCBMI.setSelected(false);
301     pickingMenu.addSeparator();
302     atomRBMI = addBGMI(levelBG, pickingMenu, "BLANK", "PickAtom", 'A', KeyEvent.VK_1, graphics);
303     bondRBMI = addBGMI(levelBG, pickingMenu, "BLANK", "PickBond", 'B', KeyEvent.VK_2, graphics);
304     angleRBMI = addBGMI(levelBG, pickingMenu, "BLANK", "PickAngle", 'N', KeyEvent.VK_3, graphics);
305     dihedralRBMI =
306         addBGMI(levelBG, pickingMenu, "BLANK", "PickDihedral", 'D', KeyEvent.VK_4, graphics);
307     residueRBMI =
308         addBGMI(levelBG, pickingMenu, "BLANK", "PickResidue", 'R', KeyEvent.VK_5, graphics);
309     polymerRBMI =
310         addBGMI(levelBG, pickingMenu, "BLANK", "PickPolymer", 'P', KeyEvent.VK_6, graphics);
311     moleculeRBMI =
312         addBGMI(levelBG, pickingMenu, "BLANK", "PickMolecule", 'M', KeyEvent.VK_7, graphics);
313     systemRBMI = addBGMI(levelBG, pickingMenu, "BLANK", "PickSystem", 'S', KeyEvent.VK_8, graphics);
314     pickingMenu.addSeparator();
315     measureDistanceRBMI =
316         addBGMI(levelBG, pickingMenu, "BLANK", "MeasureDistance", 'I', -1, graphics);
317     measureAngleRBMI = addBGMI(levelBG, pickingMenu, "BLANK", "MeasureAngle", 'L', -1, graphics);
318     measureDihedralRBMI =
319         addBGMI(levelBG, pickingMenu, "BLANK", "MeasureDihedral", 'H', -1, graphics);
320     atomRBMI.setSelected(true);
321     pickingMenu.addSeparator();
322     addMenuItem(pickingMenu, "BLANK", "SetGraphicsPickingColor", 'S', -1, graphics);
323     toolBar.addSeparator();
324 
325     // Trajectory Menu - Events handled by the MainPanel.
326     JCheckBoxMenuItem oscillateCBMI =
327         addCBMenuItem(trajectoryMenu, icons + "control_repeat_blue", "Oscillate", 'O', -1, f);
328     oscillateCBMI.setSelected(false);
329     addMenuItem(trajectoryMenu, "BLANK", "Frame", 'A', -1, f);
330     addMenuItem(trajectoryMenu, "BLANK", "Speed", 'E', -1, f);
331     addMenuItem(trajectoryMenu, "BLANK", "Skip", 'K', -1, f);
332     trajectoryMenu.addSeparator();
333     addMenuItem(trajectoryMenu, icons + "control_play_blue", "Play", 'P', -1, f);
334     addMenuItem(trajectoryMenu, icons + "control_stop_blue", "Stop", 'S', -1, f);
335     addMenuItem(trajectoryMenu, icons + "control_fastforward_blue", "StepForward", 'F', -1, f);
336     addMenuItem(trajectoryMenu, icons + "control_rewind_blue", "StepBack", 'B', -1, f);
337     addMenuItem(trajectoryMenu, icons + "control_start_blue", "Reset", 'R', -1, f);
338     toolBar.addSeparator();
339 
340     // Export Menu - Events handled by the GraphicsCanvas.
341     addMenuItem(exportMenu, icons + "camera", "CaptureGraphics", 'C', KeyEvent.VK_G, graphics);
342     exportMenu.addSeparator();
343     ButtonGroup captureFormatButtonGroup = new ButtonGroup();
344     addBGMI(captureFormatButtonGroup, exportMenu, "BLANK", "PNG", 'P', -1, graphics)
345         .setSelected(true);
346     addBGMI(captureFormatButtonGroup, exportMenu, "BLANK", "JPEG", 'J', -1, graphics);
347     addBGMI(captureFormatButtonGroup, exportMenu, "BLANK", "BMP", 'B', -1, graphics);
348     addBGMI(captureFormatButtonGroup, exportMenu, "BLANK", "WBMP", 'W', -1, graphics);
349     addBGMI(captureFormatButtonGroup, exportMenu, "BLANK", "GIF", 'G', -1, graphics);
350     toolBar.addSeparator();
351 
352     // Window Menu - Events handled by the GraphicsCanvas.
353     addMenuItem(windowMenu, icons + "application_home", "ResetPanes", 'R', -1, f);
354     addMenuItem(windowMenu, icons + "application_osx_terminal", "ResetConsole", 'L', -1, f);
355     windowMenu.addSeparator();
356     addMenuItem(
357         windowMenu, icons + "application_side_contract", "ExpandGraphicsWindow", 'E', -1, f);
358     addMenuItem(windowMenu, icons + "application_side_expand", "ShrinkGraphicsWindow", 'L', -1, f);
359     windowMenu.addSeparator();
360     systemsCBMI = addCBMenuItem(windowMenu, "BLANK", "ShowTree", 'T', -1, f);
361     toolBarCBMI = addCBMenuItem(windowMenu, "BLANK", "ShowToolBar", 'B', -1, f);
362     globalAxisCBMI = addCBMenuItem(windowMenu, "BLANK", "ShowGlobalAxes", 'C', -1, f);
363     globalAxisCBMI.setSelected(true);
364     toolBar.addSeparator();
365 
366     addMenuItem(helpMenu, "BLANK", "About", 'A', -1, f);
367   }
368 
369   /**
370    * getHighlighting
371    *
372    * @return a boolean.
373    */
374   public boolean getHighlighting() {
375     return highlightCBMI.isSelected();
376   }
377 
378   /**
379    * setHighlighting
380    *
381    * @param h a boolean.
382    */
383   void setHighlighting(boolean h) {
384     highlightCBMI.setSelected(h);
385   }
386 
387   /**
388    * getMouseMode
389    *
390    * @return a {@link ffx.ui.GraphicsCanvas.MouseMode} object.
391    */
392   public GraphicsCanvas.MouseMode getMouseMode() {
393     if (activeRBMI.isSelected()) {
394       return GraphicsCanvas.MouseMode.ACTIVESYSTEM;
395     }
396     return GraphicsCanvas.MouseMode.SYSTEMBELOWMOUSE;
397   }
398 
399   /**
400    * setMouseMode
401    *
402    * @param m a {@link ffx.ui.GraphicsCanvas.MouseMode} object.
403    */
404   void setMouseMode(GraphicsCanvas.MouseMode m) {
405     if (m == GraphicsCanvas.MouseMode.ACTIVESYSTEM) {
406       activeRBMI.doClick();
407     } else {
408       mouseRBMI.doClick();
409     }
410   }
411 
412   /**
413    * getPicking
414    *
415    * @return a boolean.
416    */
417   public boolean getPicking() {
418     return pickingCBMI.isSelected();
419   }
420 
421   /**
422    * isPickingActive
423    *
424    * @return a boolean.
425    */
426   public boolean isPickingActive() {
427     return pickingCBMI.isSelected();
428   }
429 
430   /** toggleSystemShowing */
431   public void toggleSystemShowing() {
432     systemsCBMI.doClick();
433   }
434 
435   private JMenu addMenu(String name, char mnemonic) {
436     JMenu menu = new JMenu(locale.getValue(name));
437     add(menu);
438     if (mnemonic != '.') {
439       menu.setMnemonic(mnemonic);
440     }
441     return menu;
442   }
443 
444   private Action addMenuItem(
445       JMenu menu,
446       String icon,
447       String actionCommand,
448       int mnemonic,
449       int accelerator,
450       final ActionListener actionListener) {
451     Action a =
452         new AbstractAction() {
453           @Override
454           public void actionPerformed(ActionEvent e) {
455             actionListener.actionPerformed(e);
456           }
457         };
458     configureAction(a, icon, actionCommand, mnemonic, accelerator);
459     JMenuItem menuItem = new JMenuItem(a);
460     menu.add(menuItem);
461     return a;
462   }
463 
464   private JRadioButtonMenuItem addBGMI(
465       ButtonGroup buttonGroup,
466       JMenu menu,
467       String icon,
468       String actionCommand,
469       int mnemonic,
470       int accelerator,
471       final ActionListener actionListener) {
472 
473     final JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem();
474 
475     Action a =
476         new AbstractAction() {
477           @Override
478           public void actionPerformed(ActionEvent e) {
479 
480             // If the ActionEvent is from a ToolBar button, pass it through the
481             // JRadioButtonMenuItem.
482             if (e.getSource() != menuItem) {
483               menuItem.doClick();
484               return;
485             }
486             actionListener.actionPerformed(e);
487           }
488         };
489     this.configureAction(a, icon, actionCommand, mnemonic, accelerator);
490     menuItem.setAction(a);
491     buttonGroup.add(menuItem);
492     menu.add(menuItem);
493     return menuItem;
494   }
495 
496   private JCheckBoxMenuItem addCBMenuItem(
497       JMenu menu,
498       String icon,
499       String actionCommand,
500       int mnemonic,
501       int accelerator,
502       final ActionListener actionListener) {
503 
504     final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem();
505 
506     Action a =
507         new AbstractAction() {
508           @Override
509           public void actionPerformed(ActionEvent e) {
510             // If the ActionEvent is from a ToolBar button, pass it through the JCheckBoxMenuItem.
511             if (e.getSource() != menuItem) {
512               menuItem.doClick();
513               return;
514             }
515             actionListener.actionPerformed(e);
516           }
517         };
518     configureAction(a, icon, actionCommand, mnemonic, accelerator);
519     menuItem.setAction(a);
520     menu.add(menuItem);
521     return menuItem;
522   }
523 
524   private void configureAction(
525       Action a, String icon, String actionCommand, int mnemonic, int accelerator) {
526     String name;
527     try {
528       name = locale.getValue(actionCommand);
529     } catch (Exception e) {
530       name = actionCommand;
531     }
532     ImageIcon imageIcon = getIcon(icon);
533 
534     KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator, keyMask);
535     a.putValue(ACCELERATOR_KEY, keyStroke);
536     a.putValue(ACTION_COMMAND_KEY, actionCommand);
537     a.putValue(MNEMONIC_KEY, mnemonic);
538     a.putValue(NAME, name);
539     a.putValue(SHORT_DESCRIPTION, name);
540     a.putValue(LONG_DESCRIPTION, name);
541     a.putValue(SMALL_ICON, imageIcon);
542     a.putValue(LARGE_ICON_KEY, imageIcon);
543     if (!icon.equalsIgnoreCase("BLANK")) {
544       toolBar.add(a);
545     }
546   }
547 
548   private ImageIcon getIcon(String icon) {
549     ImageIcon imageIcon = null;
550     if (!icon.equalsIgnoreCase("BLANK")) {
551       imageIcon = new ImageIcon(loader.getResource(icon + ".png"));
552     } else {
553       imageIcon = blankIcon;
554     }
555     return imageIcon;
556   }
557 
558   private JMenu addSubMenu(JMenu parent, String name, char mnemonic) {
559     JMenu menu = new JMenu(locale.getValue(name));
560     parent.add(menu);
561     if (mnemonic != '.') {
562       menu.setMnemonic(mnemonic);
563     }
564     menu.setIcon(blankIcon);
565     return menu;
566   }
567 
568   /**
569    * Get a reference the tool bar
570    *
571    * @return Force Field X ToolBar
572    */
573   JToolBar getToolBar() {
574     return toolBar;
575   }
576 
577   /**
578    * isAxisShowing
579    *
580    * @return a boolean.
581    */
582   boolean isAxisShowing() {
583     return globalAxisCBMI.isSelected();
584   }
585 
586   /**
587    * setAxisShowing
588    *
589    * @param b a boolean.
590    */
591   public void setAxisShowing(boolean b) {
592     globalAxisCBMI.setSelected(b);
593   }
594 
595   /**
596    * isMenuShowing
597    *
598    * @return a boolean.
599    */
600   boolean isMenuShowing() {
601     return toolBarCBMI.isSelected();
602   }
603 
604   /**
605    * setMenuShowing
606    *
607    * @param b a boolean.
608    */
609   void setMenuShowing(boolean b) {
610     toolBarCBMI.setSelected(b);
611   }
612 
613   /**
614    * isSystemShowing
615    *
616    * @return a boolean.
617    */
618   boolean isSystemShowing() {
619     return systemsCBMI.isSelected();
620   }
621 
622   /**
623    * setSystemShowing
624    *
625    * @param b a boolean.
626    */
627   void setSystemShowing(boolean b) {
628     systemsCBMI.setSelected(b);
629   }
630 
631   /** toggleToolBarShowing */
632   void toggleToolBarShowing() {
633     toolBarCBMI.doClick();
634   }
635 
636   /**
637    * setAtomLabels
638    *
639    * @param b a boolean.
640    */
641   void setAtomLabels(boolean b) {
642     labelAtomsMI.setSelected(b);
643   }
644 
645   /**
646    * Toggle connection status
647    *
648    * @param b a boolean.
649    */
650   void setConnect(boolean b) {
651     localMI.setEnabled(b);
652     remoteMI.setEnabled(b);
653     releaseMI.setEnabled(!b);
654   }
655 
656   /**
657    * setPickBehavior
658    *
659    * @param pick a boolean.
660    */
661   void setPickBehavior(boolean pick) {
662     pickingCBMI.setSelected(pick);
663   }
664 
665   /**
666    * setPickLevel
667    *
668    * @param arg a {@link java.lang.String} object.
669    */
670   void setPickLevel(String arg) {
671     if (arg.equals("PickAtom")) {
672       atomRBMI.doClick();
673     } else if (arg.equals("PickBond")) {
674       bondRBMI.doClick();
675     } else if (arg.equals("PickAngle")) {
676       angleRBMI.doClick();
677     } else if (arg.equals("PickDihedral")) {
678       dihedralRBMI.doClick();
679     } else if (arg.equals("PickResidue")) {
680       residueRBMI.doClick();
681     } else if (arg.equals("PickPolymer")) {
682       polymerRBMI.doClick();
683     } else if (arg.equals("PickMolecule")) {
684       moleculeRBMI.doClick();
685     } else if (arg.equals("PickSystem")) {
686       systemRBMI.doClick();
687     } else if (arg.equals("MeasureDistance")) {
688       measureDistanceRBMI.doClick();
689     } else if (arg.equals("MeasureAngle")) {
690       measureAngleRBMI.doClick();
691     } else if (arg.equals("MeasureDihedral")) {
692       measureDihedralRBMI.doClick();
693     }
694   }
695 
696   /**
697    * setResidueLabels
698    *
699    * @param b a boolean.
700    */
701   void setResidueLabels(boolean b) {
702     labelResiduesMI.setSelected(b);
703   }
704 
705   /** systemClick */
706   void systemClick() {
707     systemsCBMI.doClick();
708   }
709 }