001    // License: GPL. See LICENSE file for details.
002    package org.openstreetmap.josm.gui;
003    
004    import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005    import static org.openstreetmap.josm.tools.I18n.marktr;
006    import static org.openstreetmap.josm.tools.I18n.tr;
007    
008    import java.awt.Component;
009    import java.awt.Toolkit;
010    import java.awt.event.KeyEvent;
011    
012    import javax.swing.JCheckBoxMenuItem;
013    import javax.swing.JMenu;
014    import javax.swing.JMenuBar;
015    import javax.swing.JMenuItem;
016    import javax.swing.JPopupMenu;
017    import javax.swing.JSeparator;
018    import javax.swing.KeyStroke;
019    import javax.swing.event.MenuEvent;
020    import javax.swing.event.MenuListener;
021    
022    import org.openstreetmap.josm.Main;
023    import org.openstreetmap.josm.actions.AboutAction;
024    import org.openstreetmap.josm.actions.AddNodeAction;
025    import org.openstreetmap.josm.actions.AlignInCircleAction;
026    import org.openstreetmap.josm.actions.AlignInLineAction;
027    import org.openstreetmap.josm.actions.AutoScaleAction;
028    import org.openstreetmap.josm.actions.ChangesetManagerToggleAction;
029    import org.openstreetmap.josm.actions.CloseChangesetAction;
030    import org.openstreetmap.josm.actions.CombineWayAction;
031    import org.openstreetmap.josm.actions.CopyAction;
032    import org.openstreetmap.josm.actions.CopyCoordinatesAction;
033    import org.openstreetmap.josm.actions.CreateCircleAction;
034    import org.openstreetmap.josm.actions.CreateMultipolygonAction;
035    import org.openstreetmap.josm.actions.DeleteAction;
036    import org.openstreetmap.josm.actions.DistributeAction;
037    import org.openstreetmap.josm.actions.DownloadAction;
038    import org.openstreetmap.josm.actions.DownloadPrimitiveAction;
039    import org.openstreetmap.josm.actions.DownloadReferrersAction;
040    import org.openstreetmap.josm.actions.DuplicateAction;
041    import org.openstreetmap.josm.actions.ExitAction;
042    import org.openstreetmap.josm.actions.ExpertToggleAction;
043    import org.openstreetmap.josm.actions.FollowLineAction;
044    import org.openstreetmap.josm.actions.FullscreenToggleAction;
045    import org.openstreetmap.josm.actions.GpxExportAction;
046    import org.openstreetmap.josm.actions.HelpAction;
047    import org.openstreetmap.josm.actions.HistoryInfoAction;
048    import org.openstreetmap.josm.actions.HistoryInfoWebAction;
049    import org.openstreetmap.josm.actions.InfoAction;
050    import org.openstreetmap.josm.actions.InfoWebAction;
051    import org.openstreetmap.josm.actions.JoinAreasAction;
052    import org.openstreetmap.josm.actions.JoinNodeWayAction;
053    import org.openstreetmap.josm.actions.JosmAction;
054    import org.openstreetmap.josm.actions.JumpToAction;
055    import org.openstreetmap.josm.actions.MergeLayerAction;
056    import org.openstreetmap.josm.actions.MergeNodesAction;
057    import org.openstreetmap.josm.actions.MergeSelectionAction;
058    import org.openstreetmap.josm.actions.MirrorAction;
059    import org.openstreetmap.josm.actions.MoveAction;
060    import org.openstreetmap.josm.actions.MoveNodeAction;
061    import org.openstreetmap.josm.actions.NewAction;
062    import org.openstreetmap.josm.actions.OpenFileAction;
063    import org.openstreetmap.josm.actions.OpenLocationAction;
064    import org.openstreetmap.josm.actions.OrthogonalizeAction;
065    import org.openstreetmap.josm.actions.PasteAction;
066    import org.openstreetmap.josm.actions.PasteTagsAction;
067    import org.openstreetmap.josm.actions.PreferencesAction;
068    import org.openstreetmap.josm.actions.PurgeAction;
069    import org.openstreetmap.josm.actions.RedoAction;
070    import org.openstreetmap.josm.actions.ReverseWayAction;
071    import org.openstreetmap.josm.actions.SaveAction;
072    import org.openstreetmap.josm.actions.SaveAsAction;
073    import org.openstreetmap.josm.actions.SelectAllAction;
074    import org.openstreetmap.josm.actions.SessionLoadAction;
075    import org.openstreetmap.josm.actions.SessionSaveAsAction;
076    import org.openstreetmap.josm.actions.ShowStatusReportAction;
077    import org.openstreetmap.josm.actions.SimplifyWayAction;
078    import org.openstreetmap.josm.actions.SplitWayAction;
079    import org.openstreetmap.josm.actions.ToggleGPXLinesAction;
080    import org.openstreetmap.josm.actions.UnGlueAction;
081    import org.openstreetmap.josm.actions.UndoAction;
082    import org.openstreetmap.josm.actions.UnselectAllAction;
083    import org.openstreetmap.josm.actions.UpdateDataAction;
084    import org.openstreetmap.josm.actions.UpdateModifiedAction;
085    import org.openstreetmap.josm.actions.UpdateSelectionAction;
086    import org.openstreetmap.josm.actions.UploadAction;
087    import org.openstreetmap.josm.actions.UploadSelectionAction;
088    import org.openstreetmap.josm.actions.ViewportFollowToggleAction;
089    import org.openstreetmap.josm.actions.WireframeToggleAction;
090    import org.openstreetmap.josm.actions.ZoomInAction;
091    import org.openstreetmap.josm.actions.ZoomOutAction;
092    import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo;
093    import org.openstreetmap.josm.actions.PreferenceToggleAction;
094    import org.openstreetmap.josm.actions.audio.AudioBackAction;
095    import org.openstreetmap.josm.actions.audio.AudioFasterAction;
096    import org.openstreetmap.josm.actions.audio.AudioFwdAction;
097    import org.openstreetmap.josm.actions.audio.AudioNextAction;
098    import org.openstreetmap.josm.actions.audio.AudioPlayPauseAction;
099    import org.openstreetmap.josm.actions.audio.AudioPrevAction;
100    import org.openstreetmap.josm.actions.audio.AudioSlowerAction;
101    import org.openstreetmap.josm.actions.search.SearchAction;
102    import org.openstreetmap.josm.gui.io.RecentlyOpenedFilesMenu;
103    import org.openstreetmap.josm.gui.layer.Layer;
104    import org.openstreetmap.josm.gui.mappaint.MapPaintMenu;
105    import org.openstreetmap.josm.gui.tagging.TaggingPresetSearchAction;
106    import org.openstreetmap.josm.tools.ImageProvider;
107    import org.openstreetmap.josm.tools.Shortcut;
108    
109    /**
110     * This is the JOSM main menu bar. It is overwritten to initialize itself and provide all menu
111     * entries as member variables (sort of collect them).
112     *
113     * It also provides possibilities to attach new menu entries (used by plugins).
114     *
115     * @author Immanuel.Scholz
116     */
117    public class MainMenu extends JMenuBar {
118    
119        /* File menu */
120        public final NewAction newAction = new NewAction();
121        public final OpenFileAction openFile = new OpenFileAction();
122        public final RecentlyOpenedFilesMenu recentlyOpened = new RecentlyOpenedFilesMenu();
123        public final OpenLocationAction openLocation = new OpenLocationAction();
124        public final JosmAction save = SaveAction.getInstance();
125        public final JosmAction saveAs = SaveAsAction.getInstance();
126        public JosmAction sessionLoad;
127        public JosmAction sessionSaveAs;
128        public final JosmAction gpxExport = new GpxExportAction();
129        public final DownloadAction download = new DownloadAction();
130        public final DownloadPrimitiveAction downloadPrimitive = new DownloadPrimitiveAction();
131        public final DownloadReferrersAction downloadReferrers = new DownloadReferrersAction();
132        public final CloseChangesetAction closeChangesetAction = new CloseChangesetAction();
133        public final JosmAction update = new UpdateDataAction();
134        public final JosmAction updateSelection = new UpdateSelectionAction();
135        public final JosmAction updateModified = new UpdateModifiedAction();
136        public final JosmAction upload = new UploadAction();
137        public final JosmAction uploadSelection = new UploadSelectionAction();
138        public final JosmAction exit = new ExitAction();
139    
140        /* Edit menu */
141        public final UndoAction undo = new UndoAction();
142        public final RedoAction redo = new RedoAction();
143        public final JosmAction copy = new CopyAction();
144        public final JosmAction copyCoordinates = new CopyCoordinatesAction();
145        public final PasteAction paste = new PasteAction();
146        public final JosmAction pasteTags = new PasteTagsAction();
147        public final JosmAction duplicate = new DuplicateAction();
148        public final JosmAction delete = new DeleteAction();
149        public final JosmAction purge = new PurgeAction();
150        public final JosmAction merge = new MergeLayerAction();
151        public final JosmAction mergeSelected = new MergeSelectionAction();
152        public final JosmAction selectAll = new SelectAllAction();
153        public final JosmAction unselectAll = new UnselectAllAction();
154        public final JosmAction search = new SearchAction();
155        public final JosmAction preferences = new PreferencesAction();
156    
157        /* View menu */
158        public final WireframeToggleAction wireFrameToggleAction = new WireframeToggleAction();
159        public final JosmAction toggleGPXLines = new ToggleGPXLinesAction();
160        public final InfoAction info = new InfoAction();
161        public final InfoWebAction infoweb = new InfoWebAction();
162        public final HistoryInfoAction historyinfo = new HistoryInfoAction();
163        public final HistoryInfoWebAction historyinfoweb = new HistoryInfoWebAction();
164    
165        /* Tools menu */
166        public final JosmAction splitWay = new SplitWayAction();
167        public final JosmAction combineWay = new CombineWayAction();
168        public final JosmAction reverseWay = new ReverseWayAction();
169        public final JosmAction alignInCircle = new AlignInCircleAction();
170        public final JosmAction alignInLine = new AlignInLineAction();
171        public final JosmAction distribute = new DistributeAction();
172        public final OrthogonalizeAction ortho = new OrthogonalizeAction();
173        public final JosmAction orthoUndo = new Undo();  // action is not shown in the menu. Only triggered by shortcut
174        public final JosmAction mirror = new MirrorAction();
175        public final AddNodeAction addnode = new AddNodeAction();
176        public final MoveNodeAction movenode = new MoveNodeAction();
177        public final JosmAction createCircle = new CreateCircleAction();
178        public final JosmAction mergeNodes = new MergeNodesAction();
179        public final JosmAction joinNodeWay = new JoinNodeWayAction();
180        public final JosmAction unglueNodes = new UnGlueAction();
181        public final JosmAction simplifyWay = new SimplifyWayAction();
182        public final JosmAction joinAreas = new JoinAreasAction();
183        public final JosmAction createMultipolygon = new CreateMultipolygonAction();
184        public final JosmAction followLine = new FollowLineAction();
185    
186        /* Audio menu */
187        public final JosmAction audioPlayPause = new AudioPlayPauseAction();
188        public final JosmAction audioNext = new AudioNextAction();
189        public final JosmAction audioPrev = new AudioPrevAction();
190        public final JosmAction audioFwd = new AudioFwdAction();
191        public final JosmAction audioBack = new AudioBackAction();
192        public final JosmAction audioFaster = new AudioFasterAction();
193        public final JosmAction audioSlower = new AudioSlowerAction();
194    
195        /* Help menu */
196        public final HelpAction help = new HelpAction();
197        public final JosmAction about = new AboutAction();
198        public final JosmAction statusreport = new ShowStatusReportAction();
199    
200        public final JMenu fileMenu = addMenu(marktr("File"), KeyEvent.VK_F, 0, ht("/Menu/File"));
201        public final JMenu sessionMenu = new JMenu(tr("Session")); // submenu of the file menu
202        public final JMenu editMenu = addMenu(marktr("Edit"), KeyEvent.VK_E, 1, ht("/Menu/Edit"));
203        public final JMenu viewMenu = addMenu(marktr("View"), KeyEvent.VK_V, 2, ht("/Menu/View"));
204        public final JMenu toolsMenu = addMenu(marktr("Tools"), KeyEvent.VK_T, 3, ht("/Menu/Tools"));
205        public final JMenu presetsMenu = addMenu(marktr("Presets"), KeyEvent.VK_P, 4, ht("/Menu/Presets"));
206        public final ImageryMenu imageryMenu =
207            (ImageryMenu)addMenu(new ImageryMenu(), marktr("Imagery"), KeyEvent.VK_I, 5, ht("/Menu/Imagery"));
208        /** the window menu is split into several groups. The first is for windows that can be opened from
209         * this menu any time, e.g. the changeset editor. The second group is for toggle dialogs and the third
210         * group is for currently open windows that cannot be toggled, e.g. relation editors. It's recommended
211         * to use WINDOW_MENU_GROUP to determine the group integer.
212         */
213        public final JMenu windowMenu = addMenu(marktr("Windows"), KeyEvent.VK_W, 6, ht("/Menu/Windows"));
214        public static enum WINDOW_MENU_GROUP { ALWAYS, TOGGLE_DIALOG, VOLATILE }
215    
216        public JMenu audioMenu = null;
217        public final JMenu helpMenu = addMenu(marktr("Help"), KeyEvent.VK_H, 7, ht("/Menu/Help"));
218    
219        public final int defaultMenuPos = 7;
220    
221        public final JosmAction moveUpAction = new MoveAction(MoveAction.Direction.UP);
222        public final JosmAction moveDownAction = new MoveAction(MoveAction.Direction.DOWN);
223        public final JosmAction moveLeftAction = new MoveAction(MoveAction.Direction.LEFT);
224        public final JosmAction moveRightAction = new MoveAction(MoveAction.Direction.RIGHT);
225        public final JumpToAction jumpToAct = new JumpToAction();
226    
227        public final TaggingPresetSearchAction presetSearchAction = new TaggingPresetSearchAction();
228        public FullscreenToggleAction fullscreenToggleAction = null;
229    
230        /** this menu listener hides unnecessary JSeparators in a menu list but does not remove them.
231         * If at a later time the separators are required, they will be made visible again. Intended
232         * usage is make menus not look broken if separators are used to group the menu and some of
233         * these groups are empty.
234         */
235        public final static MenuListener menuSeparatorHandler = new MenuListener() {
236            @Override
237            public void menuCanceled(MenuEvent arg0) {}
238            @Override
239            public void menuDeselected(MenuEvent arg0) {}
240            @Override
241            public void menuSelected(MenuEvent a) {
242                if(!(a.getSource() instanceof JMenu))
243                    return;
244                final JPopupMenu m = ((JMenu) a.getSource()).getPopupMenu();
245                for(int i=0; i < m.getComponentCount()-1; i++) {
246                    if(!(m.getComponent(i) instanceof JSeparator)) {
247                        continue;
248                    }
249                    // hide separator if the next menu item is one as well
250                    ((JSeparator) m.getComponent(i)).setVisible(!(m.getComponent(i+1) instanceof JSeparator));
251                }
252                // hide separator at the end of the menu
253                if(m.getComponent(m.getComponentCount()-1) instanceof JSeparator) {
254                    ((JSeparator) m.getComponent(m.getComponentCount()-1)).setVisible(false);
255                }
256            }
257        };
258    
259        /**
260         * Add a JosmAction at the end of a menu.
261         *
262         * This method handles all the shortcut handling. It also makes sure that actions that are
263         * handled by the OS are not duplicated on the menu.
264         * @param menu the menu to add the action to
265         * @param action the action that should get a menu item
266         * @return the created menu item
267         */
268        public static JMenuItem add(JMenu menu, JosmAction action) {
269            return add(menu, action, false);
270        }
271    
272        /**
273         * Add a JosmAction at the end of a menu.
274         *
275         * This method handles all the shortcut handling. It also makes sure that actions that are
276         * handled by the OS are not duplicated on the menu.
277         * @param menu the menu to add the action to
278         * @param action the action that should get a menu item
279         * @param isExpert whether the entry should only be visible if the expert mode is activated
280         * @return the created menu item
281         */
282        public static JMenuItem add(JMenu menu, JosmAction action, boolean isExpert) {
283            return add(menu, action, isExpert, null);
284        }
285    
286        /**
287         * Add a JosmAction at the end of a menu.
288         *
289         * This method handles all the shortcut handling. It also makes sure that actions that are
290         * handled by the OS are not duplicated on the menu.
291         * @param menu the menu to add the action to
292         * @param action the action that should get a menu item
293         * @param isExpert whether the entry should only be visible if the expert mode is activated
294         * @param index  an integer specifying the position at which to add the action
295         * @return the created menu item
296         */
297        public static JMenuItem add(JMenu menu, JosmAction action, boolean isExpert, Integer index) {
298            if (action.getShortcut().getAutomatic())
299                return null;
300            final JMenuItem menuitem;
301            if (index == null) {
302                menuitem = menu.add(action);
303            } else {
304                menuitem = menu.insert(action, index);
305            }
306            if (isExpert) {
307                ExpertToggleAction.addVisibilitySwitcher(menuitem);
308            }
309            KeyStroke ks = action.getShortcut().getKeyStroke();
310            if (ks != null) {
311                menuitem.setAccelerator(ks);
312            }
313            return menuitem;
314        }
315    
316        /**
317         * Add the JosmAction {@code actionToBeInserted} directly below {@code existingMenuEntryAction}.
318         *
319         * This method handles all the shortcut handling. It also makes sure that actions that are
320         * handled by the OS are not duplicated on the menu.
321         * @param menu the menu to add the action to
322         * @param actionToBeInserted the action that should get a menu item directly below {@code existingMenuEntryAction}
323         * @param isExpert whether the entry should only be visible if the expert mode is activated
324         * @param existingMenuEntryAction an action already added to the menu {@code menu}, the action {@code actionToBeInserted} is added directly below
325         * @return the created menu item
326         */
327        public static JMenuItem addAfter(JMenu menu, JosmAction actionToBeInserted, boolean isExpert, JosmAction existingMenuEntryAction) {
328            int i = 0;
329            for (Component c : menu.getMenuComponents()) {
330                if (c instanceof JMenuItem && ((JMenuItem) c).getAction() == existingMenuEntryAction) {
331                    break;
332                }
333                i++;
334            }
335            return add(menu, actionToBeInserted, isExpert, i + 1);
336        }
337    
338        /**
339         * Add a JosmAction to a menu.
340         *
341         * This method handles all the shortcut handling. It also makes sure that actions that are
342         * handled by the OS are not duplicated on the menu.
343         * @param menu to add the action to
344         * @param action the action that should get a menu item
345         * @param group the item should be added to. Groups are split by a separator.
346         *        0 is the first group, -1 will add the item to the end.
347         */
348        public static <E extends Enum<E>> JMenuItem add(JMenu menu, JosmAction action, Enum<E> group) {
349            if (action.getShortcut().getAutomatic())
350                return null;
351            int i = getInsertionIndexForGroup(menu, group.ordinal());
352            JMenuItem menuitem = (JMenuItem) menu.add(new JMenuItem(action), i);
353            KeyStroke ks = action.getShortcut().getKeyStroke();
354            if (ks != null) {
355                menuitem.setAccelerator(ks);
356            }
357            return menuitem;
358        }
359    
360        /**
361         * Add a JosmAction to a menu and automatically prints accelerator if available.
362         * Also adds a checkbox that may be toggled.
363         * @param menu to add the action to
364         * @param action the action that should get a menu item
365         * @param group the item should be added to. Groups are split by a separator. Use
366         *        one of the enums that are defined for some of the menus to tell in which
367         *        group the item should go.
368         */
369        public static <E extends Enum<E>> JCheckBoxMenuItem addWithCheckbox(JMenu menu, JosmAction action, Enum<E> group) {
370            int i = getInsertionIndexForGroup(menu, group.ordinal());
371            final JCheckBoxMenuItem mi = (JCheckBoxMenuItem) menu.add(new JCheckBoxMenuItem(action), i);
372            final KeyStroke ks = action.getShortcut().getKeyStroke();
373            if (ks != null) {
374                mi.setAccelerator(ks);
375            }
376            return mi;
377        }
378    
379        /** finds the correct insertion index for a given group and adds separators if necessary */
380        private static int getInsertionIndexForGroup(JMenu menu, int group) {
381            if(group < 0)
382                return -1;
383            // look for separator that *ends* the group (or stop at end of menu)
384            int i;
385            for(i=0; i < menu.getItemCount() && group >= 0; i++) {
386                if(menu.getItem(i) == null) {
387                    group--;
388                }
389            }
390            // insert before separator that ends the group
391            if(group < 0) {
392                i--;
393            }
394            // not enough separators have been found, add them
395            while(group > 0) {
396                menu.addSeparator();
397                group--;
398                i++;
399            }
400            return i;
401        }
402    
403        public JMenu addMenu(String name, int mnemonicKey, int position, String relativeHelpTopic) {
404            final JMenu menu = new JMenu(tr(name));
405            int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
406            int menuItemHeight = new JMenu().add(newAction).getPreferredSize().height;
407            MenuScroller.setScrollerFor(menu, screenHeight / menuItemHeight);
408            return addMenu(menu, name, mnemonicKey, position, relativeHelpTopic);
409        }
410    
411        public JMenu addMenu(JMenu menu, String name, int mnemonicKey, int position, String relativeHelpTopic) {
412            Shortcut.registerShortcut("menu:" + name, tr("Menu: {0}", tr(name)), mnemonicKey,
413                    Shortcut.MNEMONIC).setMnemonic(menu);
414            add(menu, position);
415            menu.putClientProperty("help", relativeHelpTopic);
416            return menu;
417        }
418    
419        public MainMenu() {
420            JMenuItem current;
421    
422            add(fileMenu, newAction);
423            add(fileMenu, openFile);
424            fileMenu.add(recentlyOpened);
425            add(fileMenu, openLocation);
426            fileMenu.addSeparator();
427            add(fileMenu, save);
428            add(fileMenu, saveAs);
429            if (Main.pref.getBoolean("session")) {
430                sessionMenu.setToolTipText(tr("Save and load the current session (list of layers, etc.)"));
431                sessionMenu.setIcon(ImageProvider.get("session"));
432                sessionSaveAs = new SessionSaveAsAction();
433                sessionLoad = new SessionLoadAction();
434                add(sessionMenu, sessionSaveAs);
435                add(sessionMenu, sessionLoad);
436                fileMenu.add(sessionMenu);
437            }
438            add(fileMenu, gpxExport, true);
439            fileMenu.addSeparator();
440            add(fileMenu, download);
441            add(fileMenu, downloadPrimitive);
442            add(fileMenu, downloadReferrers);
443            add(fileMenu, update);
444            add(fileMenu, updateSelection);
445            add(fileMenu, updateModified);
446            fileMenu.addSeparator();
447            add(fileMenu, upload);
448            add(fileMenu, uploadSelection);
449            Component sep = new JPopupMenu.Separator();
450            fileMenu.add(sep);
451            ExpertToggleAction.addVisibilitySwitcher(sep);
452            add(fileMenu, closeChangesetAction, true);
453            fileMenu.addSeparator();
454            add(fileMenu, exit);
455    
456            add(editMenu, undo);
457            Main.main.undoRedo.addCommandQueueListener(undo);
458            add(editMenu, redo);
459            Main.main.undoRedo.addCommandQueueListener(redo);
460            editMenu.addSeparator();
461            add(editMenu, copy);
462            add(editMenu, copyCoordinates, true);
463            add(editMenu, paste);
464            add(editMenu, pasteTags);
465            add(editMenu, duplicate);
466            add(editMenu, delete);
467            add(editMenu, purge, true);
468            editMenu.addSeparator();
469            add(editMenu,merge);
470            add(editMenu,mergeSelected);
471            editMenu.addSeparator();
472            add(editMenu, selectAll);
473            add(editMenu, unselectAll);
474            editMenu.addSeparator();
475            add(editMenu, search);
476            editMenu.addSeparator();
477            add(editMenu, preferences);
478    
479            // -- wireframe toggle action
480            final JCheckBoxMenuItem wireframe = new JCheckBoxMenuItem(wireFrameToggleAction);
481            viewMenu.add(wireframe);
482            wireframe.setAccelerator(wireFrameToggleAction.getShortcut().getKeyStroke());
483            wireFrameToggleAction.addButtonModel(wireframe.getModel());
484    
485            viewMenu.add(new MapPaintMenu());
486            viewMenu.addSeparator();
487            add(viewMenu, new ZoomInAction());
488            add(viewMenu, new ZoomOutAction());
489            viewMenu.addSeparator();
490            for (String mode : AutoScaleAction.MODES) {
491                JosmAction autoScaleAction = new AutoScaleAction(mode);
492                add(viewMenu, autoScaleAction);
493            }
494    
495            // -- viewport follow toggle action
496            ViewportFollowToggleAction viewportFollowToggleAction = new ViewportFollowToggleAction();
497            final JCheckBoxMenuItem vft = new JCheckBoxMenuItem(viewportFollowToggleAction);
498            ExpertToggleAction.addVisibilitySwitcher(vft);
499            viewMenu.add(vft);
500            vft.setAccelerator(viewportFollowToggleAction.getShortcut().getKeyStroke());
501            viewportFollowToggleAction.addButtonModel(vft.getModel());
502    
503            if(!Main.applet && Main.platform.canFullscreen()) {
504                // -- fullscreen toggle action
505                fullscreenToggleAction = new FullscreenToggleAction();
506                final JCheckBoxMenuItem fullscreen = new JCheckBoxMenuItem(fullscreenToggleAction);
507                viewMenu.addSeparator();
508                viewMenu.add(fullscreen);
509                fullscreen.setAccelerator(fullscreenToggleAction.getShortcut().getKeyStroke());
510                fullscreenToggleAction.addButtonModel(fullscreen.getModel());
511            }
512            viewMenu.addSeparator();
513            add(viewMenu, info);
514            add(viewMenu, infoweb);
515            add(viewMenu, historyinfo);
516            add(viewMenu, historyinfoweb);
517            viewMenu.addSeparator();
518            viewMenu.add(new PreferenceToggleAction(tr("Edit toolbar"),
519                    tr("Toggles the visibility of the edit toolbar (i.e., the vertical tool)"),
520                    "sidetoolbar.visible", true).getCheckbox());
521            // -- expert mode toggle action
522            final JCheckBoxMenuItem expertItem = new JCheckBoxMenuItem(ExpertToggleAction.getInstance());
523            viewMenu.add(expertItem);
524            ExpertToggleAction.getInstance().addButtonModel(expertItem.getModel());
525    
526            add(presetsMenu, presetSearchAction);
527            presetsMenu.addSeparator();
528    
529            add(toolsMenu, splitWay);
530            add(toolsMenu, combineWay);
531            toolsMenu.addSeparator();
532            add(toolsMenu, reverseWay);
533            add(toolsMenu, simplifyWay);
534            toolsMenu.addSeparator();
535            add(toolsMenu, alignInCircle);
536            add(toolsMenu, alignInLine);
537            add(toolsMenu, distribute);
538            add(toolsMenu, ortho);
539            add(toolsMenu, mirror, true);
540            toolsMenu.addSeparator();
541            add(toolsMenu, followLine, true);
542            add(toolsMenu, addnode, true);
543            add(toolsMenu, movenode, true);
544            add(toolsMenu, createCircle);
545            toolsMenu.addSeparator();
546            add(toolsMenu, mergeNodes);
547            add(toolsMenu, joinNodeWay);
548            add(toolsMenu, unglueNodes);
549            toolsMenu.addSeparator();
550            add(toolsMenu, joinAreas);
551            add(toolsMenu, createMultipolygon);
552    
553            // -- changeset manager toggle action
554            ChangesetManagerToggleAction changesetManagerToggleAction = new ChangesetManagerToggleAction();
555            final JCheckBoxMenuItem mi = MainMenu.addWithCheckbox(windowMenu, changesetManagerToggleAction,
556                    MainMenu.WINDOW_MENU_GROUP.ALWAYS);
557            changesetManagerToggleAction.addButtonModel(mi.getModel());
558    
559    
560            if (!Main.pref.getBoolean("audio.menuinvisible", false)) {
561                audioMenu = addMenu(marktr("Audio"), KeyEvent.VK_U, defaultMenuPos, ht("/Menu/Audio"));
562                add(audioMenu, audioPlayPause);
563                add(audioMenu, audioNext);
564                add(audioMenu, audioPrev);
565                add(audioMenu, audioFwd);
566                add(audioMenu, audioBack);
567                add(audioMenu, audioSlower);
568                add(audioMenu, audioFaster);
569            }
570    
571            helpMenu.add(statusreport);
572    
573            current = helpMenu.add(help); // FIXME why is help not a JosmAction?
574            current.setAccelerator(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1,
575                    Shortcut.DIRECT).getKeyStroke());
576            add(helpMenu, about);
577    
578    
579            windowMenu.addMenuListener(menuSeparatorHandler);
580    
581            new PresetsMenuEnabler(presetsMenu).refreshEnabled();
582        }
583    
584        static class PresetsMenuEnabler implements MapView.LayerChangeListener {
585            private JMenu presetsMenu;
586            public PresetsMenuEnabler(JMenu presetsMenu) {
587                MapView.addLayerChangeListener(this);
588                this.presetsMenu = presetsMenu;
589            }
590            /**
591             * Refreshes the enabled state
592             *
593             */
594            protected void refreshEnabled() {
595                presetsMenu.setEnabled(Main.isDisplayingMapView()
596                        && Main.map.mapView.getEditLayer() != null
597                );
598            }
599    
600            public void activeLayerChange(Layer oldLayer, Layer newLayer) {
601                refreshEnabled();
602            }
603    
604            public void layerAdded(Layer newLayer) {
605                refreshEnabled();
606            }
607    
608            public void layerRemoved(Layer oldLayer) {
609                refreshEnabled();
610            }
611        }
612    }