package org.vorthmann.zome.ui; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileInputStream; import java.util.Map; import java.util.Properties; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.ButtonGroup; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JRadioButtonMenuItem; import javax.swing.KeyStroke; import org.vorthmann.j3d.Platform; import org.vorthmann.ui.Controller; public class DocumentMenuBar extends JMenuBar implements PropertyChangeListener { private static final int COMMAND = Platform.getKeyModifierMask(); private static final int COMMAND_OPTION = COMMAND | InputEvent.ALT_MASK; private static final int COMMAND_SHIFT = COMMAND | InputEvent.SHIFT_MASK; private static final int CONTROL = InputEvent.CTRL_MASK; private static final int CONTROL_OPTION = InputEvent.CTRL_MASK | InputEvent.ALT_MASK; private static final long serialVersionUID = 1L; private final JMenuItem setColorMenuItem, showToolsMenuItem, zomicMenuItem, pythonMenuItem, importVEFItem; private final ControlActions actions; private final boolean fullPower; public DocumentMenuBar( final Controller controller, ControlActions actions ) { this .actions = actions; controller .addPropertyListener( this ); String initSystem = controller .getProperty( "symmetry" ); String fieldName = controller .getProperty( "field.name" ); // TODO: compute these booleans once in DocumentFrame, and don't recompute here boolean readerPreview = controller .propertyIsTrue( "reader.preview" ); boolean isEditor = controller .userHasEntitlement( "model.edit" ) && ! readerPreview; this .fullPower = isEditor; // && controller .userHasEntitlement( "all.tools" ); boolean developerExtras = controller .userHasEntitlement( "developer.extras" ); boolean enable4d = developerExtras || ( fullPower && controller .userHasEntitlement( "4d.symmetries" ) ); boolean metaModels = developerExtras || ( fullPower && controller .userHasEntitlement( "meta.models" ) ); boolean canSave = controller .userHasEntitlement( "save.files" ); boolean isGolden = "golden" .equals( fieldName ); boolean isHeptagon = "heptagon" .equals( fieldName ); boolean isSnubDodec = "snubDodec" .equals( fieldName ); boolean isRootTwo = "rootTwo" .equals( fieldName ); boolean isRootThree = "rootThree" .equals( fieldName ); boolean oldTools = controller .propertyIsTrue( "original.tools" ); // ----------------------------------------- File menu JMenu menu = new JMenu( "File" ); if ( fullPower ) { JMenu submenu = new JMenu( "New Model..." ); submenu.add( enableIf( isEditor || readerPreview, createMenuItem( "Zome (Golden) Field", "new-golden", KeyEvent.VK_N, COMMAND ) ) ); String[] fieldNames = controller .getCommandList( "fields" ); for ( String fName : fieldNames ) { if ( controller .propertyIsTrue( "enable." + fName + ".field" ) ) { String label = controller .getProperty( "field.label." + fName ); submenu .add( createMenuItem( label + " Field", "new-" + fName ) ); } } menu.add( submenu ); } else { menu.add( createMenuItem( "New Model...", "new", KeyEvent.VK_N, COMMAND ) ); } menu.add( createMenuItem( "Open...", "open", KeyEvent.VK_O, COMMAND ) ); menu.add( createMenuItem( "Open URL...", "openURL" ) ); menu.add( createMenuItem( "Open As New Model...", "newFromTemplate" ) ); if ( developerExtras ) menu.add( createMenuItem( "Open Deferred...", "openDeferringRedo" ) ); menu.addSeparator(); menu.add( createMenuItem( "Close", "close", KeyEvent.VK_W, COMMAND ) ); menu .add( enableIf( canSave, createMenuItem( "Save...", "save", KeyEvent.VK_S, COMMAND ) ) ); menu .add( enableIf( canSave, createMenuItem( "Save As...", "saveAs" ) ) ); menu .add( enableIf( canSave, createMenuItem( "Save Template...", "saveDefault" ) ) ); menu.addSeparator(); importVEFItem = createMenuItem( "Import vZome VEF...", "import.vef" ); menu .add( importVEFItem ); JMenu submenu = new JMenu( "Export 3D Rendering..." ); submenu .add( createMenuItem( "Collada DAE", "export.dae" ) ); submenu .add( createMenuItem( "POV-Ray", "export.pov" ) ); submenu .add( createMenuItem( "WebGL JSON", "export.json" ) ); submenu .add( createMenuItem( "VRML", "export.vrml" ) ); menu.add( submenu ); submenu .setEnabled( fullPower && canSave ); submenu = new JMenu( "Export 3D Geometry..." ); submenu .add( createMenuItem( "StL", "export.StL" ) ); submenu .add( createMenuItem( "PLY", "export.ply" ) ); submenu .add( createMenuItem( "vZome VEF", "export.vef" ) ); submenu .add( createMenuItem( "STEP", "export.step" ) ); submenu .add( createMenuItem( "OFF", "export.off" ) ); submenu .add( createMenuItem( "AutoCAD DXF", "export.dxf" ) ); if ( controller .userHasEntitlement( "export.pdb" ) ) { submenu .add( createMenuItem( "PDB", "export.pdb" ) ); } if ( controller .userHasEntitlement( "export.seg" ) ) { submenu .add( createMenuItem( "Mark Stock .seg", "export.seg" ) ); } menu.add( submenu ); submenu .setEnabled( fullPower && canSave ); if ( developerExtras ) { submenu = new JMenu( "Export Developer Extras..." ); submenu .addSeparator(); submenu.add( createMenuItem( "Second Life", "export.2life" ) ); submenu.add( createMenuItem( "Maximum XYZ", "export.size" ) ); submenu.add( createMenuItem( "vZome part geometry", "export.partgeom" ) ); submenu.add( createMenuItem( "vZome history detail", "export.history" ) ); submenu.add( createMenuItem( "bill of materials", "export.partslist" ) ); menu.add( submenu ); submenu .setEnabled( fullPower && canSave ); } menu.addSeparator(); // if ( controller .userHasEntitlement( "export.zomespace" ) ) // { // submenu = new JMenu( "Export Article..." ); // submenu .add( createMenuItem( "Zomespace", "export.zomespace" ) ); // menu.add( submenu ); // submenu .setEnabled( fullPower && canSave ); // } submenu = new JMenu( "Capture Image..." ); submenu .add( createMenuItem( "JPEG", "capture.jpg" ) ); submenu .add( createMenuItem( "PNG", "capture.png" ) ); submenu .add( createMenuItem( "GIF", "capture.gif" ) ); submenu .add( createMenuItem( "BMP", "capture.bmp" ) ); menu.add( submenu ); menu .add( createMenuItem( "Capture Animation...", "capture-animation" ) ); menu.add( enableIf( isEditor, createMenuItem( "Capture PDF or SVG...", "snapshot.2d" ) ) ); menu.addSeparator(); menu.add( createMenuItem( "Quit", "quit", KeyEvent.VK_Q, COMMAND ) ); super .add( menu ); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Edit menu menu = new JMenu( "Edit" ); menu.add( enableIf( isEditor, createMenuItem( "Undo", "undo", KeyEvent.VK_Z, COMMAND ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Redo", ( "redo" ), KeyEvent.VK_Y, COMMAND ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Undo All", ( "undoAll" ), KeyEvent.VK_Z, COMMAND_OPTION ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Redo All", ( "redoAll" ), KeyEvent.VK_Y, COMMAND_OPTION ) ) ); if ( developerExtras ) { menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu .add( createMenuItem( "Undo To Breakpoint", ( "undoToBreakpoint" ), KeyEvent.VK_B, COMMAND_SHIFT ) ); menu .add( createMenuItem( "Redo To Breakpoint", ( "redoToBreakpoint" ), KeyEvent.VK_B, COMMAND_OPTION ) ); menu .add( createMenuItem( "Set Breakpoint", ( "setBreakpoint" ), KeyEvent.VK_B, COMMAND ) ); menu .add( createMenuItem( "Redo to Edit Number...", "redoUntilEdit" ) ); } menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu .add( enableIf( isEditor, createMenuItem( "Cut", ( "cut" ), KeyEvent.VK_X, COMMAND ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Copy", ( "copy" ), KeyEvent.VK_C, COMMAND ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Paste", ( "paste" ), KeyEvent.VK_V, COMMAND ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Delete", ( "delete" ), KeyEvent.VK_BACK_SPACE, 0 ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu .add( enableIf( isEditor, createMenuItem( "Select All", ( "selectAll" ), KeyEvent.VK_A, COMMAND ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Select Neighbors", ( "selectNeighbors" ), KeyEvent.VK_A, COMMAND_OPTION ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Invert Selection", ( "invertSelection" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Deselect Balls", ( "unselectBalls" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Deselect Struts and Panels", ( "unselectStruts" ) ) ) ); submenu = new JMenu("Select..."); submenu.add( enableIf( isEditor, createMenuItem( "All Collinear", ( "SelectCollinear" ) ) ) ); submenu.add( enableIf( isEditor, createMenuItem( "Parallel Struts", ( "SelectParallelStruts" ) ) ) ); submenu.add( enableIf( isEditor, createMenuItem( "Automatic Struts", ( "SelectAutomaticStruts" ) ) ) ); // submenu .add( createMenuItem( "First Octant", getExclusiveAction( "test.pick.cube" ) ) ); menu.add(submenu); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu .add( enableIf( isEditor, createMenuItem( "Group", ( "group" ), KeyEvent.VK_G, COMMAND ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Ungroup", ( "ungroup" ), KeyEvent.VK_G, COMMAND_OPTION ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu .add( enableIf( isEditor, createMenuItem( "Hide", ( "hideball" ), KeyEvent.VK_H, CONTROL ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Show All Hidden", ( "showHidden" ), KeyEvent.VK_H, CONTROL_OPTION ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% setColorMenuItem = enableIf( isEditor, createMenuItem( "Set Color...", "setItemColor" ) ); menu .add( setColorMenuItem ); super .add( menu ); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Construct menu menu = new JMenu( "Construct" ); menu.add( enableIf( isEditor, createMenuItem( "Loop Balls", "joinballs", KeyEvent.VK_J, COMMAND ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Chain Balls", "chainBalls", KeyEvent.VK_J, COMMAND_OPTION ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Join Balls to Last", "joinBallsAllToLast" ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Make All Possible Struts", "joinBallsAllPossible" ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu .add( enableIf( isEditor, createMenuItem( "Panel", ( "panel" ), KeyEvent.VK_P, COMMAND ) ) ); menu .add( enableIf( isEditor, createMenuItem( "Panel/Strut Vertices", ( "showVertices" ) ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu.add( enableIf( isEditor, createMenuItem( "Centroid", ( "centroid" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Strut Midpoint", ( "midpoint" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Line-Line Intersection", ( "lineLineIntersect" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Line-Plane Intersection", ( "linePlaneIntersect" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Cross Product", ( "crossProduct" ) ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu.add( enableIf( isEditor, createMenuItem( "Ball At Origin", ( "ballAtOrigin" ) ) ) ); if ( controller .propertyIsTrue( "original.tools" ) ) menu.add( enableIf( isEditor, createMenuItem( "Ball At Symmetry Center", ( "ballAtSymmCenter" ) ) ) ); menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // menu.add( enableIf( isEditor, createMenuItem( "Affine Transform All", getExclusiveAction( "affineTransformAll" ) ) ); // menuItem = enableIf( isEditor, createMenuItem( "Conjugate", getExclusiveAction( "conjugate" ) ); if ( metaModels ) { menu .add( createMenuItem( "Meta-model", ( "realizeMetaParts" ) ) ); } if ( isGolden ) { menu.add( enableIf( isEditor, createMenuItem( "\u03C6 Divide", ( "tauDivide" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Affine Pentagon", ( "affinePentagon" ) ) ) ); } else if ( isHeptagon ) { menu.add( enableIf( isEditor, createMenuItem( "1/\u03C3/\u03C1 Subdivisions", ( "heptagonDivide" ) ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Affine Heptagon", ( "affineHeptagon" ) ) ) ); } if ( developerExtras ) { menu .addSeparator(); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% menu.add( enableIf( isEditor, createMenuItem( "Assert Selection", ( "assertSelection" ) ) ) ); // menu.add( enableIf( isEditor, createMenuItem( "6-Lattice", getExclusiveAction( "sixLattice" ) ) ); } super .add( menu ); // ----------------------------------------- Tools menu menu = new JMenu( "Tools" ); if ( oldTools ) { menu.add( enableIf( isEditor, createMenuItem( "Set Center", "setSymmetryCenter" ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Set Axis", "setSymmetryAxis" ) ) ); menu.addSeparator(); showToolsMenuItem = enableIf( isEditor, createMenuItem( "Show Tools Panel", "showToolsPanel" ) ); showToolsMenuItem .setEnabled( fullPower ); menu .add( showToolsMenuItem ); menu .addSeparator(); } else this .showToolsMenuItem = null; if ( isGolden ) { menu.add( enableIf( isEditor, createMenuItem( "Icosahedral Symmetry", "icosasymm-golden", KeyEvent.VK_I, COMMAND ) ) ); } else if ( isSnubDodec ) menu.add( enableIf( isEditor, createMenuItem( "Icosahedral Symmetry", "icosasymm-snubDodec", KeyEvent.VK_I, COMMAND ) ) ); if ( developerExtras && isRootThree ) { menu.add( enableIf( isEditor, createMenuItem( "Dodecagonal Symmetry", "dodecagonsymm", KeyEvent.VK_D, COMMAND ) ) ); } menu.add( enableIf( isEditor, createMenuItem( "Cubic / Octahedral Symmetry", "octasymm", KeyEvent.VK_C, COMMAND_OPTION ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Tetrahedral Symmetry", "tetrasymm", KeyEvent.VK_T, COMMAND_OPTION ) ) ); if ( oldTools ) { menu.add( enableIf( isEditor, createMenuItem( "Axial Symmetry", "axialsymm", KeyEvent.VK_R, COMMAND ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Mirror Reflection", "mirrorsymm" , KeyEvent.VK_M, COMMAND ) ) ); menu.add( enableIf( isEditor, createMenuItem( "Translate", "translate", KeyEvent.VK_T, COMMAND ) ) ); } menu.add( enableIf( isEditor, createMenuItem( "Point Reflection", "pointsymm" ) ) ); menu .addSeparator(); menu .add( enableIf( isEditor, createMenuItem( "Generate Polytope...", "showPolytopesDialog", KeyEvent.VK_P, COMMAND_OPTION ) ) ); if ( enable4d ) { menu.add( enableIf( isEditor, createMenuItem( "H_4 Symmetry", "h4symmetry" ) ) ); menu.add( enableIf( isEditor, createMenuItem( "H_4 Rotations", "h4rotations" ) ) ); menu.add( enableIf( isEditor, createMenuItem( "I,T Symmetry", "IxTsymmetry" ) ) ); menu.add( enableIf( isEditor, createMenuItem( "T,T Symmetry", "TxTsymmetry" ) ) ); } super .add( menu ); // ----------------------------------------- System menu menu = new JMenu( "System" ); ButtonGroup group = new ButtonGroup(); JMenuItem rbMenuItem; if ( isGolden || isSnubDodec ) { rbMenuItem = actions .setMenuAction( "setSymmetry.icosahedral", new JRadioButtonMenuItem( "Icosahedral System" ) ); rbMenuItem .setSelected( "icosahedral".equals( initSystem ) ); rbMenuItem .setEnabled( fullPower ); group.add( rbMenuItem ); menu.add( rbMenuItem ); } else if ( isHeptagon ) { rbMenuItem = actions .setMenuAction( "setSymmetry.heptagonal antiprism", new JRadioButtonMenuItem( "Heptagonal Antiprism System" ) ); rbMenuItem .setSelected( "heptagonal antiprism".equals( initSystem ) ); rbMenuItem .setEnabled( fullPower ); group.add( rbMenuItem ); menu.add( rbMenuItem ); rbMenuItem = actions .setMenuAction( "setSymmetry.triangular antiprism", new JRadioButtonMenuItem( "Triangular Antiprism System" ) ); rbMenuItem .setSelected( "triangular antiprism".equals( initSystem ) ); rbMenuItem .setEnabled( fullPower ); group.add( rbMenuItem ); // DISABLED until the symmetry group has been properly implemented // menu.add( rbMenuItem ); } rbMenuItem = actions .setMenuAction( "setSymmetry.octahedral", new JRadioButtonMenuItem( "Octahedral System" ) ); rbMenuItem .setSelected( "octahedral".equals( initSystem ) ); rbMenuItem .setEnabled( fullPower ); group.add( rbMenuItem ); menu.add( rbMenuItem ); if ( isRootThree ) { rbMenuItem = actions .setMenuAction( "setSymmetry.dodecagonal", new JRadioButtonMenuItem( "Dodecagon System" ) ); rbMenuItem .setSelected( "dodecagonal".equals( initSystem ) ); rbMenuItem .setEnabled( fullPower ); group.add( rbMenuItem ); menu.add( rbMenuItem ); } else if ( isRootTwo ) { rbMenuItem = actions .setMenuAction( "setSymmetry.synestructics", new JRadioButtonMenuItem( "Synestructics System" ) ); rbMenuItem .setSelected( "synestructics".equals( initSystem ) ); rbMenuItem .setEnabled( fullPower ); group.add( rbMenuItem ); menu.add( rbMenuItem ); } menu.addSeparator(); if ( developerExtras ) { JMenuItem wfMenuItem = actions .setMenuAction( "toggleWireframe", new JCheckBoxMenuItem( "Wireframe" ) ); boolean isWireframe = "true".equals( controller .getProperty( "wireframe" ) ); wfMenuItem .setSelected( isWireframe ); menu.add( wfMenuItem ); } menu.add( createMenuItem( "Shapes...", "configureShapes" ) ); menu.add( createMenuItem( "Directions...", "configureDirections" ) ); JMenuItem cbMenuItem = actions .setMenuAction( "toggleOrbitViews", new JCheckBoxMenuItem( "Show Directions Graphically" ) ); boolean setting = "true".equals( controller .getProperty( "useGraphicalViews" ) ); cbMenuItem .setSelected( setting ); menu.add( cbMenuItem ); cbMenuItem .setEnabled( fullPower ); final JMenuItem showStrutScalesItem = actions .setMenuAction( "toggleStrutScales", new JCheckBoxMenuItem( "Show Strut Scales" ) ); setting = "true" .equals( controller .getProperty( "showStrutScales" ) ); showStrutScalesItem .setSelected( setting ); showStrutScalesItem .setEnabled( fullPower ); controller .addPropertyListener( new PropertyChangeListener(){ @Override public void propertyChange( PropertyChangeEvent chg ) { if ( "showStrutScales" .equals( chg .getPropertyName() ) ) showStrutScalesItem .setSelected(((Boolean) chg .getNewValue())); }} ); menu .add( showStrutScalesItem ); // cbMenuItem = enabler .enableMenuAction( "toggleOneSidedPanels", new JCheckBoxMenuItem( "Show Panels One-sided" ) ); // setting = "true".equals( controller.getProperty( "oneSidedPanels" ) ); // cbMenuItem.setSelected( setting ); // menu.add( cbMenuItem ); cbMenuItem = actions .setMenuAction( "toggleFrameLabels", new JCheckBoxMenuItem( "Show Frame Labels" ) ); setting = "true".equals( controller .getProperty( "showFrameLabels" ) ); cbMenuItem .setSelected( setting ); menu.add( cbMenuItem ); cbMenuItem = actions .setMenuAction( "toggleOutlines", new JCheckBoxMenuItem( "Render Outlines" ) ); setting = "true".equals( controller.getProperty( "drawOutlines" ) ); cbMenuItem .setSelected( setting ); menu.add( cbMenuItem ); super.add( menu ); // ----------------------------------------- Scripting menu menu = new JMenu( "Scripting" ); menu .setEnabled( fullPower ); pythonMenuItem = createMenuItem( "Python...", "showPythonWindow" ); pythonMenuItem .setEnabled( fullPower ); if ( developerExtras ) menu .add( pythonMenuItem ); zomicMenuItem = createMenuItem( "Zomic...", "showZomicWindow" ); zomicMenuItem .setEnabled( fullPower ); menu .add( zomicMenuItem ); super .add( menu ); // ----------------------------------------- Custom menu menu = getCustomMenu(); if(menu != null) { super .add( menu ); } // ----------------------------------------- Help menu menu = new JMenu( "Help" ); if ( "G4G10" .equals( controller .getProperty( "licensed.user" ) ) ) menu .add( createMenuItem( "Welcome G4G10 Participant...", "openResource-org/vorthmann/zome/content/welcomeG4G10.vZome" ) ); menu .add( createMenuItem( "Quick Start...", "openResource-org/vorthmann/zome/content/welcomeDodec.vZome" ) ); menu .addSeparator(); menu .add( createMenuItem( "About vZome...", "showAbout" ) ); super .add( menu ); } @Override public void propertyChange( PropertyChangeEvent e ) { if ( "editor.mode" .equals( e .getPropertyName() ) ) { String mode = (String) e .getNewValue(); if ( "article" .equals( mode ) ) { setColorMenuItem .setEnabled( false ); if ( showToolsMenuItem != null ) showToolsMenuItem .setEnabled( false ); pythonMenuItem .setEnabled( false ); zomicMenuItem .setEnabled( false ); importVEFItem .setEnabled( false ); } else { setColorMenuItem .setEnabled( true ); if ( showToolsMenuItem != null ) showToolsMenuItem .setEnabled( fullPower ); pythonMenuItem .setEnabled( fullPower ); zomicMenuItem .setEnabled( fullPower ); importVEFItem .setEnabled( fullPower ); } } } private JMenuItem enableIf( boolean enable, JMenuItem control ) { control .setEnabled( enable ); return control; } private JMenuItem createMenuItem( String label, String command ) { return createMenuItem( label, command, KeyEvent .CHAR_UNDEFINED, 0 ); } private JMenuItem createMenuItem( String label, String command, int key, int modifiers ) { JMenuItem menuItem = actions .setMenuAction( command, new JMenuItem( label ) ); menuItem .setEnabled( true ); if ( key != KeyEvent .CHAR_UNDEFINED ) menuItem .setAccelerator( KeyStroke.getKeyStroke( key, modifiers ) ); return menuItem; } /** * @return JMenu with custom menu items added or null if no custom menu items are defined. * * Creates a custom menu from a properties formatted file in the user's preferences folder. * * There is no regard for how the custom commands will be processed at runtime. * The menu items are added unconditionally and are not context sensitive. * For example, they are loaded without regard for whether they will work with a particular model's field. * * The keys read from the file become the commands to be processed and the values become the menu item's text. * * If the menu item's text contains a caret immediately followed by a number (e.g. "^6") * then that number with the COMMAND key modifier are enabled as a menu accelerator. * The caret and number are removed from the menu item's text. * The caret can occur anywhere in the menu item's text. * Sorting of the menu items occurs before the menu accelerators are processed, * so they can optionally be used to sort the menu items depending on their placement in the string. * No checks are made for duplicate menu accelerator assignments, but Java handles them reasonably if they occur. For example, the file might contain these entries: assertSelection = Assert Selection sixLattice = 6-Lattice ^6 somePrereleasedFeature = TODO: Unreleased Feature test.pick.cube = ^8 First Octant */ private JMenu getCustomMenu() { File customMenuFile = new File(Platform.getPreferencesFolder(), "vZomeCustomMenu.properties"); try { if (customMenuFile.exists()) { Properties customMenuItems = new Properties(); customMenuItems.load(new FileInputStream(customMenuFile)); if (!customMenuItems.isEmpty()) { // Properties collection is unaffected by the order the items occur in the file // so we'll sort them by menuText for consistent placement on the menu. // Note that a case insensitive sort is specified. // Menu items in the properties file can be prefixed with numbers to generate a particular display order Map<String, String> sortedMenuCommands = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (Object key : customMenuItems.keySet()) { String actionName = key.toString(); String menuText = customMenuItems.getProperty(actionName).trim(); if(!menuText.isEmpty()) { // also note that we're swapping keys for elements // as we move from the Properties collection to the sortedMenuCommands sortedMenuCommands.put(menuText, actionName); } } if (! sortedMenuCommands.isEmpty() ) { JMenu menu = new JMenu("Custom"); for (String label : sortedMenuCommands.keySet()) { JMenuItem menuItem = null; String command = sortedMenuCommands.get(label); int caretPos = label.indexOf("^"); if(caretPos > -1 && caretPos < label.length()-1) { int key = label.charAt(caretPos + 1); if(key >= KeyEvent.VK_0 && key <= KeyEvent.VK_9) { label = label.replace("^" + (key - KeyEvent.VK_0), "").trim(); menuItem = createMenuItem(label, command, key, COMMAND); } } if(menuItem == null) { menuItem = createMenuItem(label, command); } menu.add(menuItem); } return menu; } } } } catch (Exception ex) { log("Error loading custom menu from " + customMenuFile.getAbsolutePath(), ex); } return null; } private void log(String msg, Exception ex) { // TODO: cleanup this error reporting... Maybe pass this info back to the controller via reportError() Logger.getLogger(getClass().getName()).log(Level.WARNING, msg, ex); } }