]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/gui/MenuManager.java
Use data from viewfinderpanoramas.org
[GpsPrune.git] / src / tim / prune / gui / MenuManager.java
1 package tim.prune.gui;
2
3 import java.awt.Toolkit;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.awt.event.KeyEvent;
7
8 import javax.swing.JButton;
9 import javax.swing.JCheckBoxMenuItem;
10 import javax.swing.JMenu;
11 import javax.swing.JMenuBar;
12 import javax.swing.JMenuItem;
13 import javax.swing.JToolBar;
14 import javax.swing.KeyStroke;
15
16 import tim.prune.App;
17 import tim.prune.DataSubscriber;
18 import tim.prune.FunctionLibrary;
19 import tim.prune.GenericFunction;
20 import tim.prune.I18nManager;
21 import tim.prune.UpdateMessageBroker;
22 import tim.prune.config.Config;
23 import tim.prune.data.AudioClip;
24 import tim.prune.data.DataPoint;
25 import tim.prune.data.Field;
26 import tim.prune.data.Photo;
27 import tim.prune.data.RecentFile;
28 import tim.prune.data.RecentFileList;
29 import tim.prune.data.Selection;
30 import tim.prune.data.Track;
31 import tim.prune.data.TrackInfo;
32 import tim.prune.function.ChooseSingleParameter;
33 import tim.prune.function.SearchOpenCachingDeFunction;
34 import tim.prune.function.browser.UrlGenerator;
35 import tim.prune.function.browser.WebMapFunction;
36 import tim.prune.function.search.SearchMapillaryFunction;
37 import tim.prune.function.srtm.DownloadSrtmFunction;
38 import tim.prune.function.srtm.SrtmGl1Source;
39 import tim.prune.function.srtm.Srtm3Source;
40 import tim.prune.function.srtm.SrtmViewfinderSource;
41
42 /**
43  * Class to manage the menu bar and tool bar,
44  * including enabling and disabling the items
45  */
46 public class MenuManager implements DataSubscriber
47 {
48         private App _app = null;
49         private Track _track = null;
50         private Selection _selection = null;
51
52         // Menu items which need enabling/disabling
53         private JMenuItem _sendGpsItem = null;
54         private JMenuItem _saveItem = null;
55         private JMenuItem _exportKmlItem = null;
56         private JMenuItem _exportGpxItem = null;
57         private JMenuItem _exportPovItem = null;
58         private JMenuItem _exportImageItem = null;
59         private JMenu     _recentFileMenu = null;
60         private JMenuItem _undoItem = null;
61         private JMenuItem _clearUndoItem = null;
62         private JMenuItem _editPointItem = null;
63         private JMenuItem _editWaypointNameItem = null;
64         private JMenuItem _deletePointItem = null;
65         private JMenuItem _deleteRangeItem = null;
66         private JMenuItem _cropTrackItem = null;
67         private JMenuItem _compressItem = null;
68         private JMenuItem _markRectangleItem = null;
69         private JMenuItem _markUphillLiftsItem = null;
70         private JMenuItem _deleteMarkedPointsItem = null;
71         private JMenuItem _deleteByDateItem = null;
72         private JMenuItem _interpolateItem = null;
73         private JMenuItem _averageItem = null;
74         private JMenuItem _selectAllItem = null;
75         private JMenuItem _selectNoneItem = null;
76         private JMenuItem _selectSegmentItem = null;
77         private JMenuItem _selectStartItem = null;
78         private JMenuItem _selectEndItem = null;
79         private JMenuItem _findWaypointItem = null;
80         private JMenuItem _duplicatePointItem = null;
81         private JMenuItem _reverseItem = null;
82         private JMenuItem _addTimeOffsetItem = null;
83         private JMenuItem _addAltitudeOffsetItem = null;
84         private JMenuItem _removeAltitudesItem = null;
85         private JMenuItem _mergeSegmentsItem = null;
86         private JMenuItem _rearrangeWaypointsItem = null;
87         private JMenuItem _splitSegmentsItem = null;
88         private JMenuItem _sewSegmentsItem = null;
89         private JMenuItem _createMarkerWaypointsItem = null;
90         private JMenuItem _cutAndMoveItem = null;
91         private JMenuItem _convertNamesToTimesItem = null;
92         private JMenuItem _deleteFieldValuesItem = null;
93         private JCheckBoxMenuItem _mapCheckbox = null;
94         private JMenuItem _show3dItem = null;
95         private JMenu     _browserMapMenu = null;
96         private JMenuItem _routingGraphHopperItem = null;
97         private JMenuItem _chartItem = null;
98         private JMenuItem _getGpsiesItem = null;
99         private JMenuItem _uploadGpsiesItem = null;
100         private JMenuItem _lookupSrtmItem = null;
101         private JMenu     _downloadSrtmMenu = null;
102         private JMenuItem _nearbyWikipediaItem = null;
103         private JMenuItem _nearbyOsmPoiItem = null;
104         private JMenuItem _showPeakfinderItem = null;
105         private JMenuItem _showGeohackItem = null;
106         private JMenuItem _searchOpencachingDeItem = null;
107         private JMenuItem _searchMapillaryItem = null;
108         private JMenuItem _downloadOsmItem = null;
109         private JMenuItem _getWeatherItem = null;
110         private JMenuItem _distanceItem = null;
111         private JMenuItem _fullRangeDetailsItem = null;
112         private JMenuItem _estimateTimeItem = null;
113         private JMenuItem _learnEstimationParams = null;
114         private JMenuItem _autoplayTrack = null;
115         private JMenuItem _saveExifItem = null;
116         private JMenuItem _photoPopupItem = null;
117         private JMenuItem _selectNoPhotoItem = null;
118         private JMenuItem _connectPhotoItem = null;
119         private JMenuItem _removePhotoItem = null;
120         private JMenuItem _disconnectPhotoItem = null;
121         private JMenuItem _correlatePhotosItem = null;
122         private JMenuItem _rearrangePhotosItem = null;
123         private JMenuItem _rotatePhotoLeft = null;
124         private JMenuItem _rotatePhotoRight = null;
125         private JMenuItem _ignoreExifThumb = null;
126         private JMenuItem _connectAudioItem = null;
127         private JMenuItem _disconnectAudioItem = null;
128         private JMenuItem _removeAudioItem = null;
129         private JMenuItem _correlateAudiosItem = null;
130         private JMenuItem _selectNoAudioItem = null;
131         private JCheckBoxMenuItem _onlineCheckbox = null;
132         private JCheckBoxMenuItem _autosaveSettingsCheckbox = null;
133
134         // ActionListeners for reuse by menu and toolbar
135         private ActionListener _openFileAction = null;
136         private ActionListener _addPhotoAction = null;
137         private ActionListener _saveAction = null;
138         private ActionListener _undoAction = null;
139         private ActionListener _editPointAction = null;
140         private ActionListener _deletePointAction = null;
141         private ActionListener _selectStartAction = null;
142         private ActionListener _selectEndAction = null;
143
144         // Toolbar buttons which need enabling/disabling
145         private JButton _saveButton = null;
146         private JButton _undoButton = null;
147         private JButton _editPointButton = null;
148         private JButton _deletePointButton = null;
149         private JButton _deleteRangeButton = null;
150         private JButton _cutAndMoveButton = null;
151         private JButton _selectStartButton = null;
152         private JButton _selectEndButton = null;
153         private JButton _connectButton = null;
154
155         /** Array of key events */
156         private static final int[] KEY_EVENTS = {
157                 KeyEvent.VK_A, KeyEvent.VK_B, KeyEvent.VK_C, KeyEvent.VK_D, KeyEvent.VK_E,
158                 KeyEvent.VK_F, KeyEvent.VK_G, KeyEvent.VK_H, KeyEvent.VK_I, KeyEvent.VK_J,
159                 KeyEvent.VK_K, KeyEvent.VK_L, KeyEvent.VK_M, KeyEvent.VK_N, KeyEvent.VK_O,
160                 KeyEvent.VK_P, KeyEvent.VK_Q, KeyEvent.VK_R, KeyEvent.VK_S, KeyEvent.VK_T,
161                 KeyEvent.VK_U, KeyEvent.VK_V, KeyEvent.VK_W, KeyEvent.VK_X, KeyEvent.VK_Y, KeyEvent.VK_Z};
162
163
164         /**
165          * Constructor
166          * @param inApp application to call on menu actions
167          * @param inTrackInfo track info object
168          */
169         public MenuManager(App inApp, TrackInfo inTrackInfo)
170         {
171                 _app = inApp;
172                 _track = inTrackInfo.getTrack();
173                 _selection = inTrackInfo.getSelection();
174         }
175
176
177         /**
178          * Create a JMenuBar containing all menu items
179          * @return JMenuBar
180          */
181         public JMenuBar createMenuBar()
182         {
183                 JMenuBar menubar = new JMenuBar();
184                 JMenu fileMenu = new JMenu(I18nManager.getText("menu.file"));
185                 setAltKey(fileMenu, "altkey.menu.file");
186                 // Open file
187                 JMenuItem openMenuItem = new JMenuItem(I18nManager.getText("function.open"));
188                 setShortcut(openMenuItem, "shortcut.menu.file.open");
189                 _openFileAction = new ActionListener() {
190                         public void actionPerformed(ActionEvent e) {
191                                 _app.openFile();
192                         }
193                 };
194                 openMenuItem.addActionListener(_openFileAction);
195                 fileMenu.add(openMenuItem);
196                 // import through gpsbabel
197                 JMenuItem importBabelItem = makeMenuItem(FunctionLibrary.FUNCTION_IMPORTBABEL);
198                 fileMenu.add(importBabelItem);
199                 // Add photos
200                 JMenuItem addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos"));
201                 _addPhotoAction = new ActionListener() {
202                         public void actionPerformed(ActionEvent e) {
203                                 _app.addPhotos();
204                         }
205                 };
206                 addPhotosMenuItem.addActionListener(_addPhotoAction);
207                 fileMenu.add(addPhotosMenuItem);
208                 // Add audio clips
209                 JMenuItem addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
210                 fileMenu.add(addAudioMenuItem);
211                 // recent files
212                 _recentFileMenu = new JMenu(I18nManager.getText("menu.file.recentfiles"));
213                 _recentFileMenu.setEnabled(false);
214                 fileMenu.add(_recentFileMenu);
215                 fileMenu.addSeparator();
216                 // Load from GPS
217                 JMenuItem loadFromGpsMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSLOAD);
218                 setShortcut(loadFromGpsMenuItem, "shortcut.menu.file.load");
219                 fileMenu.add(loadFromGpsMenuItem);
220                 // Send to GPS
221                 _sendGpsItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSSAVE, false);
222                 fileMenu.add(_sendGpsItem);
223                 fileMenu.addSeparator();
224                 // Save
225                 _saveItem = new JMenuItem(I18nManager.getText("menu.file.save"), KeyEvent.VK_S);
226                 setShortcut(_saveItem, "shortcut.menu.file.save");
227                 _saveAction = new ActionListener() {
228                         public void actionPerformed(ActionEvent e) {
229                                 _app.saveFile();
230                         }
231                 };
232                 _saveItem.addActionListener(_saveAction);
233                 _saveItem.setEnabled(false);
234                 fileMenu.add(_saveItem);
235                 // Export - Kml
236                 _exportKmlItem = makeMenuItem(FunctionLibrary.FUNCTION_KMLEXPORT, false);
237                 fileMenu.add(_exportKmlItem);
238                 // Gpx
239                 _exportGpxItem = makeMenuItem(FunctionLibrary.FUNCTION_GPXEXPORT, false);
240                 fileMenu.add(_exportGpxItem);
241                 // Pov
242                 _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT, false);
243                 fileMenu.add(_exportPovItem);
244                 // Image
245                 _exportImageItem = makeMenuItem(FunctionLibrary.FUNCTION_IMAGEEXPORT, false);
246                 fileMenu.add(_exportImageItem);
247                 fileMenu.addSeparator();
248                 // Exit
249                 JMenuItem exitMenuItem = new JMenuItem(I18nManager.getText("menu.file.exit"));
250                 exitMenuItem.addActionListener(new ActionListener() {
251                         public void actionPerformed(ActionEvent e) {
252                                 _app.exit();
253                         }
254                 });
255                 fileMenu.add(exitMenuItem);
256                 menubar.add(fileMenu);
257
258                 ////////////////////////////////////////////////////
259                 // Online menu
260                 JMenu onlineMenu = new JMenu(I18nManager.getText("menu.online"));
261                 setAltKey(onlineMenu, "altkey.menu.online");
262                 // SRTM
263                 _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
264                 onlineMenu.add(_lookupSrtmItem);
265                 // Download SRTM sub-menu
266                 _downloadSrtmMenu = new JMenu(I18nManager.getText("function.downloadsrtm"));
267                 _downloadSrtmMenu.setEnabled(false);
268                 JMenuItem downloadStrmGl1Item = makeMenuItem(new DownloadSrtmFunction(_app, new SrtmGl1Source()));
269                 _downloadSrtmMenu.add(downloadStrmGl1Item);
270                 JMenuItem downloadStrmViewfinderItem = makeMenuItem(new DownloadSrtmFunction(_app, new SrtmViewfinderSource()));
271                 _downloadSrtmMenu.add(downloadStrmViewfinderItem);
272                 JMenuItem downloadStrm3Item = makeMenuItem(new DownloadSrtmFunction(_app, new Srtm3Source()));
273                 _downloadSrtmMenu.add(downloadStrm3Item);
274                 onlineMenu.add(_downloadSrtmMenu);
275
276                 // Get gpsies tracks
277                 _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
278                 onlineMenu.add(_getGpsiesItem);
279                 // Upload to gpsies
280                 _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
281                 onlineMenu.add(_uploadGpsiesItem);
282
283                 onlineMenu.addSeparator();
284                 // browser submenu
285                 _browserMapMenu = new JMenu(I18nManager.getText("menu.view.browser"));
286                 _browserMapMenu.setEnabled(false);
287                 JMenuItem googleMapsItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_GOOGLE, "menu.view.browser.google"));
288                 _browserMapMenu.add(googleMapsItem);
289                 JMenuItem openMapsItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_OSM, "menu.view.browser.openstreetmap"));
290                 _browserMapMenu.add(openMapsItem);
291                 JMenuItem mapquestMapsItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_MAPQUEST, "menu.view.browser.mapquest"));
292                 _browserMapMenu.add(mapquestMapsItem);
293                 JMenuItem yahooMapsItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_YAHOO, "menu.view.browser.yahoo"));
294                 _browserMapMenu.add(yahooMapsItem);
295                 JMenuItem bingMapsItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_BING, "menu.view.browser.bing"));
296                 _browserMapMenu.add(bingMapsItem);
297                 JMenuItem inlineMapItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_INLINESKATE, "menu.view.browser.inlinemap"));
298                 _browserMapMenu.add(inlineMapItem);
299                 _routingGraphHopperItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_GRAPHHOPPER, "menu.view.browser.graphhopper"));
300                 _browserMapMenu.add(_routingGraphHopperItem);
301                 onlineMenu.add(_browserMapMenu);
302                 // wikipedia
303                 _nearbyWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_NEARBY_WIKIPEDIA, false);
304                 onlineMenu.add(_nearbyWikipediaItem);
305                 JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
306                 onlineMenu.add(searchWikipediaNamesItem);
307                 _nearbyOsmPoiItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_OSMPOIS);
308                 onlineMenu.add(_nearbyOsmPoiItem);
309                 _showPeakfinderItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_PEAKFINDER, "webservice.peakfinder"), false);
310                 onlineMenu.add(_showPeakfinderItem);
311                 _showGeohackItem = makeMenuItem(new WebMapFunction(_app, UrlGenerator.WebService.MAP_SOURCE_GEOHACK, "webservice.geohack"), false);
312                 onlineMenu.add(_showGeohackItem);
313
314                 onlineMenu.addSeparator();
315                 _searchOpencachingDeItem = makeMenuItem(new SearchOpenCachingDeFunction(_app), false);
316                 onlineMenu.add(_searchOpencachingDeItem);
317                 _searchMapillaryItem = makeMenuItem(new SearchMapillaryFunction(_app), false);
318                 onlineMenu.add(_searchMapillaryItem);
319                 _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
320                 onlineMenu.add(_downloadOsmItem);
321                 _getWeatherItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_WEATHER_FORECAST, false);
322                 onlineMenu.add(_getWeatherItem);
323                 menubar.add(onlineMenu);
324
325                 ////////////////////////////////////////////////////
326                 // Track menu
327                 JMenu trackMenu = new JMenu(I18nManager.getText("menu.track"));
328                 setAltKey(trackMenu, "altkey.menu.track");
329                 _undoItem = new JMenuItem(I18nManager.getText("menu.track.undo"));
330                 setShortcut(_undoItem, "shortcut.menu.track.undo");
331                 _undoAction = new ActionListener() {
332                         public void actionPerformed(ActionEvent e) {
333                                 _app.beginUndo();
334                         }
335                 };
336                 _undoItem.addActionListener(_undoAction);
337                 _undoItem.setEnabled(false);
338                 trackMenu.add(_undoItem);
339                 _clearUndoItem = new JMenuItem(I18nManager.getText("menu.track.clearundo"));
340                 _clearUndoItem.addActionListener(new ActionListener() {
341                         public void actionPerformed(ActionEvent e) {
342                                 _app.clearUndo();
343                         }
344                 });
345                 _clearUndoItem.setEnabled(false);
346                 trackMenu.add(_clearUndoItem);
347                 trackMenu.addSeparator();
348                 _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS, false);
349                 setShortcut(_compressItem, "shortcut.menu.track.compress");
350                 trackMenu.add(_compressItem);
351                 _markRectangleItem = new JMenuItem(I18nManager.getText("menu.track.markrectangle"));
352                 _markRectangleItem.addActionListener(new ActionListener() {
353                         public void actionPerformed(ActionEvent e) {
354                                 _app.setCurrentMode(App.AppMode.DRAWRECT);
355                                 UpdateMessageBroker.informSubscribers();
356                         }
357                 });
358                 _markRectangleItem.setEnabled(false);
359                 trackMenu.add(_markRectangleItem);
360                 _markUphillLiftsItem = makeMenuItem(FunctionLibrary.FUNCTION_MARK_LIFTS, false);
361                 trackMenu.add(_markUphillLiftsItem);
362                 _deleteMarkedPointsItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_MARKED_POINTS, false);
363                 trackMenu.add(_deleteMarkedPointsItem);
364                 _deleteByDateItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_BY_DATE, false);
365                 trackMenu.add(_deleteByDateItem);
366                 trackMenu.addSeparator();
367                 // Rearrange waypoints
368                 _rearrangeWaypointsItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS, false);
369                 trackMenu.add(_rearrangeWaypointsItem);
370                 // Split track segments
371                 _splitSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SPLIT_SEGMENTS, false);
372                 trackMenu.add(_splitSegmentsItem);
373                 // Sew track segments
374                 _sewSegmentsItem = makeMenuItem(FunctionLibrary.FUNCTION_SEW_SEGMENTS, false);
375                 trackMenu.add(_sewSegmentsItem);
376                 // Create marker waypoints
377                 _createMarkerWaypointsItem = makeMenuItem(FunctionLibrary.FUNCTION_CREATE_MARKER_WAYPOINTS, false);
378                 trackMenu.add(_createMarkerWaypointsItem);
379                 trackMenu.addSeparator();
380                 _learnEstimationParams = makeMenuItem(FunctionLibrary.FUNCTION_LEARN_ESTIMATION_PARAMS, false);
381                 trackMenu.add(_learnEstimationParams);
382                 menubar.add(trackMenu);
383
384                 // Range menu
385                 JMenu rangeMenu = new JMenu(I18nManager.getText("menu.range"));
386                 setAltKey(rangeMenu, "altkey.menu.range");
387                 _selectAllItem = new JMenuItem(I18nManager.getText("menu.range.all"));
388                 setShortcut(_selectAllItem, "shortcut.menu.range.all");
389                 _selectAllItem.setEnabled(false);
390                 _selectAllItem.addActionListener(new ActionListener() {
391                         public void actionPerformed(ActionEvent e) {
392                                 _selection.selectRange(0, _track.getNumPoints()-1);
393                         }
394                 });
395                 rangeMenu.add(_selectAllItem);
396                 _selectNoneItem = new JMenuItem(I18nManager.getText("menu.range.none"));
397                 _selectNoneItem.setEnabled(false);
398                 _selectNoneItem.addActionListener(new ActionListener() {
399                         public void actionPerformed(ActionEvent e) {
400                                 _app.selectNone();
401                         }
402                 });
403                 rangeMenu.add(_selectNoneItem);
404                 _selectSegmentItem = makeMenuItem(FunctionLibrary.FUNCTION_SELECT_SEGMENT);
405                 rangeMenu.add(_selectSegmentItem);
406                 rangeMenu.addSeparator();
407                 _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start"));
408                 _selectStartItem.setEnabled(false);
409                 _selectStartAction = new ActionListener() {
410                         public void actionPerformed(ActionEvent e) {
411                                 _selection.selectRangeStart();
412                         }
413                 };
414                 _selectStartItem.addActionListener(_selectStartAction);
415                 rangeMenu.add(_selectStartItem);
416                 _selectEndItem = new JMenuItem(I18nManager.getText("menu.range.end"));
417                 _selectEndItem.setEnabled(false);
418                 _selectEndAction = new ActionListener() {
419                         public void actionPerformed(ActionEvent e) {
420                                 _selection.selectRangeEnd();
421                         }
422                 };
423                 _selectEndItem.addActionListener(_selectEndAction);
424                 rangeMenu.add(_selectEndItem);
425                 rangeMenu.addSeparator();
426                 _deleteRangeItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_RANGE, false);
427                 rangeMenu.add(_deleteRangeItem);
428                 _cropTrackItem = makeMenuItem(FunctionLibrary.FUNCTION_CROP_TRACK, false);
429                 rangeMenu.add(_cropTrackItem);
430                 _reverseItem = new JMenuItem(I18nManager.getText("menu.range.reverse"));
431                 _reverseItem.addActionListener(new ActionListener() {
432                         public void actionPerformed(ActionEvent e) {
433                                 _app.reverseRange();
434                         }
435                 });
436                 _reverseItem.setEnabled(false);
437                 rangeMenu.add(_reverseItem);
438                 _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET, false);
439                 rangeMenu.add(_addTimeOffsetItem);
440                 _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET, false);
441                 rangeMenu.add(_addAltitudeOffsetItem);
442                 _removeAltitudesItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_ALTITUDES, false);
443                 rangeMenu.add(_removeAltitudesItem);
444                 _mergeSegmentsItem = new JMenuItem(I18nManager.getText("menu.range.mergetracksegments"));
445                 _mergeSegmentsItem.addActionListener(new ActionListener() {
446                         public void actionPerformed(ActionEvent e) {
447                                 _app.mergeTrackSegments();
448                         }
449                 });
450                 _mergeSegmentsItem.setEnabled(false);
451                 rangeMenu.add(_mergeSegmentsItem);
452                 _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false);
453                 rangeMenu.add(_deleteFieldValuesItem);
454                 rangeMenu.addSeparator();
455                 _interpolateItem = makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_INTERPOLATE), false);
456                 rangeMenu.add(_interpolateItem);
457                 _averageItem = new JMenuItem(I18nManager.getText("menu.range.average"));
458                 _averageItem.addActionListener(new ActionListener() {
459                         public void actionPerformed(ActionEvent e) {
460                                 _app.averageSelection();
461                         }
462                 });
463                 _averageItem.setEnabled(false);
464                 rangeMenu.add(_averageItem);
465                 _cutAndMoveItem = new JMenuItem(I18nManager.getText("menu.range.cutandmove"));
466                 _cutAndMoveItem.addActionListener(new ActionListener() {
467                         public void actionPerformed(ActionEvent e)
468                         {
469                                 _app.cutAndMoveSelection();
470                         }
471                 });
472                 _cutAndMoveItem.setEnabled(false);
473                 rangeMenu.add(_cutAndMoveItem);
474                 _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES, false);
475                 rangeMenu.add(_convertNamesToTimesItem);
476                 menubar.add(rangeMenu);
477
478                 // Point menu
479                 JMenu pointMenu = new JMenu(I18nManager.getText("menu.point"));
480                 setAltKey(pointMenu, "altkey.menu.point");
481                 _editPointItem = new JMenuItem(I18nManager.getText("menu.point.editpoint"));
482                 _editPointAction = new ActionListener() {
483                         public void actionPerformed(ActionEvent e) {
484                                 _app.editCurrentPoint();
485                         }
486                 };
487                 _editPointItem.addActionListener(_editPointAction);
488                 _editPointItem.setEnabled(false);
489                 setShortcut(_editPointItem, "shortcut.menu.point.edit");
490                 pointMenu.add(_editPointItem);
491                 _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME, false);
492                 pointMenu.add(_editWaypointNameItem);
493                 _deletePointItem = new JMenuItem(I18nManager.getText("menu.point.deletepoint"));
494                 _deletePointAction = new ActionListener() {
495                         public void actionPerformed(ActionEvent e) {
496                                 _app.deleteCurrentPoint();
497                         }
498                 };
499                 _deletePointItem.addActionListener(_deletePointAction);
500                 _deletePointItem.setEnabled(false);
501                 _deletePointItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
502                 pointMenu.add(_deletePointItem);
503                 pointMenu.addSeparator();
504                 // find a waypoint
505                 _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT, false);
506                 pointMenu.add(_findWaypointItem);
507                 // duplicate current point
508                 _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT, false);
509                 pointMenu.add(_duplicatePointItem);
510                 // paste coordinates function
511                 JMenuItem pasteCoordsItem = makeMenuItem(FunctionLibrary.FUNCTION_PASTE_COORDINATES);
512                 pointMenu.add(pasteCoordsItem);
513                 menubar.add(pointMenu);
514
515                 // Add view menu
516                 JMenu viewMenu = new JMenu(I18nManager.getText("menu.view"));
517                 setAltKey(viewMenu, "altkey.menu.view");
518                 // Turn map display on/off
519                 _mapCheckbox = new JCheckBoxMenuItem(
520                         I18nManager.getText("menu.map.showmap"), false);
521                 _mapCheckbox.addActionListener(new ActionListener() {
522                         public void actionPerformed(ActionEvent e) {
523                                 Config.setConfigBoolean(Config.KEY_SHOW_MAP, _mapCheckbox.isSelected());
524                                 UpdateMessageBroker.informSubscribers(MAPSERVER_CHANGED);
525                         }
526                 });
527                 viewMenu.add(_mapCheckbox);
528                 // Turn off the sidebars
529                 JCheckBoxMenuItem sidebarsCheckbox = new JCheckBoxMenuItem(I18nManager.getText("menu.view.showsidebars"));
530                 sidebarsCheckbox.setSelected(true);
531                 sidebarsCheckbox.addActionListener(new ActionListener() {
532                         public void actionPerformed(ActionEvent e) {
533                                 _app.toggleSidebars();
534                         }
535                 });
536                 sidebarsCheckbox.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); // shortcut F11
537                 viewMenu.add(sidebarsCheckbox);
538                 // 3d
539                 _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D, false);
540                 viewMenu.add(_show3dItem);
541                 // Charts
542                 _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS, false);
543                 viewMenu.add(_chartItem);
544                 viewMenu.addSeparator();
545                 // Distances
546                 _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES, false);
547                 viewMenu.add(_distanceItem);
548                 // full range details
549                 _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS, false);
550                 viewMenu.add(_fullRangeDetailsItem);
551                 // estimate time
552                 _estimateTimeItem = makeMenuItem(FunctionLibrary.FUNCTION_ESTIMATE_TIME, false);
553                 viewMenu.add(_estimateTimeItem);
554                 viewMenu.addSeparator();
555                 // autoplay
556                 _autoplayTrack = makeMenuItem(FunctionLibrary.FUNCTION_AUTOPLAY_TRACK, false);
557                 viewMenu.add(_autoplayTrack);
558                 menubar.add(viewMenu);
559
560                 // Add photo menu
561                 JMenu photoMenu = new JMenu(I18nManager.getText("menu.photo"));
562                 setAltKey(photoMenu, "altkey.menu.photo");
563                 addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos"));
564                 addPhotosMenuItem.addActionListener(_addPhotoAction);
565                 photoMenu.add(addPhotosMenuItem);
566                 _saveExifItem = new JMenuItem(I18nManager.getText("menu.photo.saveexif"));
567                 _saveExifItem.addActionListener(new ActionListener() {
568                         public void actionPerformed(ActionEvent e) {
569                                 _app.saveExif();
570                         }
571                 });
572                 _saveExifItem.setEnabled(false);
573                 photoMenu.add(_saveExifItem);
574                 // Deselect current photo
575                 _selectNoPhotoItem = new JMenuItem(I18nManager.getText("menu.range.none"));
576                 _selectNoPhotoItem.setEnabled(false);
577                 _selectNoPhotoItem.addActionListener(new ActionListener() {
578                         public void actionPerformed(ActionEvent e) {
579                                 _app.getTrackInfo().selectPhoto(-1);
580                         }
581                 });
582                 photoMenu.add(_selectNoPhotoItem);
583                 photoMenu.addSeparator();
584                 _connectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
585                 photoMenu.add(_connectPhotoItem);
586                 // disconnect photo
587                 _disconnectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_PHOTO, false);
588                 photoMenu.add(_disconnectPhotoItem);
589                 _removePhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_PHOTO, false);
590                 photoMenu.add(_removePhotoItem);
591                 // Rotate current photo
592                 _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT, false);
593                 photoMenu.add(_rotatePhotoLeft);
594                 _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT, false);
595                 photoMenu.add(_rotatePhotoRight);
596                 // Show photo popup
597                 _photoPopupItem = makeMenuItem(FunctionLibrary.FUNCTION_PHOTO_POPUP, false);
598                 photoMenu.add(_photoPopupItem);
599                 _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB, false);
600                 photoMenu.add(_ignoreExifThumb);
601                 photoMenu.addSeparator();
602                 // correlate all photos
603                 _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS, false);
604                 photoMenu.add(_correlatePhotosItem);
605                 // rearrange photo points
606                 _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS, false);
607                 photoMenu.add(_rearrangePhotosItem);
608                 menubar.add(photoMenu);
609
610                 // Audio menu
611                 JMenu audioMenu = new JMenu(I18nManager.getText("menu.audio"));
612                 setAltKey(audioMenu, "altkey.menu.audio");
613                 addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
614                 audioMenu.add(addAudioMenuItem);
615                 _selectNoAudioItem = new JMenuItem(I18nManager.getText("menu.range.none"));
616                 _selectNoAudioItem.setEnabled(false);
617                 _selectNoAudioItem.addActionListener(new ActionListener() {
618                         public void actionPerformed(ActionEvent e) {
619                                 _app.getTrackInfo().selectAudio(-1);
620                         }
621                 });
622                 audioMenu.add(_selectNoAudioItem);
623                 audioMenu.addSeparator();
624                 // connect audio
625                 _connectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
626                 audioMenu.add(_connectAudioItem);
627                 // Disconnect current audio clip
628                 _disconnectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_AUDIO, false);
629                 audioMenu.add(_disconnectAudioItem);
630                 // Remove current audio clip
631                 _removeAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_AUDIO, false);
632                 audioMenu.add(_removeAudioItem);
633                 audioMenu.addSeparator();
634                 // Correlate audio clips
635                 _correlateAudiosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_AUDIOS, false);
636                 audioMenu.add(_correlateAudiosItem);
637                 menubar.add(audioMenu);
638
639                 // Settings menu
640                 JMenu settingsMenu = new JMenu(I18nManager.getText("menu.settings"));
641                 setAltKey(settingsMenu, "altkey.menu.settings");
642                 // Set the map background
643                 JMenuItem mapBgItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_MAP_BG);
644                 settingsMenu.add(mapBgItem);
645                 _onlineCheckbox = new JCheckBoxMenuItem(I18nManager.getText("menu.settings.onlinemode"));
646                 _onlineCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_ONLINE_MODE));
647                 _onlineCheckbox.addActionListener(new ActionListener() {
648                         public void actionPerformed(ActionEvent e) {
649                                 boolean isOnline = _onlineCheckbox.isSelected();
650                                 Config.setConfigBoolean(Config.KEY_ONLINE_MODE, isOnline);
651                                 if (isOnline) {UpdateMessageBroker.informSubscribers();}
652                         }
653                 });
654                 settingsMenu.add(_onlineCheckbox);
655                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_DISK_CACHE));
656                 settingsMenu.addSeparator();
657                 // Set program paths
658                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_PATHS));
659                 // Set colours
660                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS));
661                 // display settings
662                 JMenuItem setDisplaySettingsItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_DISPLAY_SETTINGS);
663                 settingsMenu.add(setDisplaySettingsItem);
664                 // Set language
665                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE));
666                 // Set altitude tolerance
667                 settingsMenu.add(makeMenuItem(new ChooseSingleParameter(_app, FunctionLibrary.FUNCTION_SET_ALTITUDE_TOLERANCE)));
668                 // Set timezone
669                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_TIMEZONE));
670                 // Set Earthdata authentication
671                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_EARTHDATA_AUTH));
672                 settingsMenu.addSeparator();
673                 // Save configuration
674                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG));
675                 _autosaveSettingsCheckbox = new JCheckBoxMenuItem(
676                         I18nManager.getText("menu.settings.autosave"), false);
677                 _autosaveSettingsCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS));
678                 _autosaveSettingsCheckbox.addActionListener(new ActionListener() {
679                         public void actionPerformed(ActionEvent e) {
680                                 Config.setConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS, _autosaveSettingsCheckbox.isSelected());
681                         }
682                 });
683                 settingsMenu.add(_autosaveSettingsCheckbox);
684                 menubar.add(settingsMenu);
685
686                 // Help menu
687                 JMenu helpMenu = new JMenu(I18nManager.getText("menu.help"));
688                 setAltKey(helpMenu, "altkey.menu.help");
689                 JMenuItem helpItem = makeMenuItem(FunctionLibrary.FUNCTION_HELP);
690                 setShortcut(helpItem, "shortcut.menu.help.help");
691                 helpMenu.add(helpItem);
692                 helpMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SHOW_KEYS));
693                 helpMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_ABOUT));
694                 helpMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_CHECK_VERSION));
695                 menubar.add(helpMenu);
696
697                 return menubar;
698         }
699
700         /**
701          * Convenience method for making a menu item using a function
702          * @param inFunction function
703          * @param inEnabled flag to specify initial enabled state
704          * @return menu item using localized name of function
705          */
706         private static JMenuItem makeMenuItem(GenericFunction inFunction, boolean inEnabled)
707         {
708                 JMenuItem item = makeMenuItem(inFunction);
709                 item.setEnabled(inEnabled);
710                 return item;
711         }
712
713         /**
714          * Convenience method for making a menu item using a function
715          * @param inFunction function
716          * @return menu item using localized name of function
717          */
718         private static JMenuItem makeMenuItem(GenericFunction inFunction)
719         {
720                 JMenuItem item = new JMenuItem(I18nManager.getText(inFunction.getNameKey()));
721                 item.addActionListener(new FunctionLauncher(inFunction));
722                 return item;
723         }
724
725         /**
726          * Set the alt key for the given menu
727          * @param inMenu menu to set
728          * @param inKey key to lookup to get language-sensitive altkey
729          */
730         private static void setAltKey(JMenu inMenu, String inKey)
731         {
732                 // Lookup the key in the properties
733                 String altKey = I18nManager.getText(inKey);
734                 if (altKey != null && altKey.length() == 1)
735                 {
736                         int code = altKey.charAt(0) - 'A';
737                         if (code >= 0 && code < 26)
738                         {
739                                 // Found a valid code between A and Z
740                                 inMenu.setMnemonic(KEY_EVENTS[code]);
741                         }
742                 }
743         }
744
745         /**
746          * Set the shortcut key for the given menu item
747          * @param inMenuItem menu item to set
748          * @param inKey key to lookup to get language-sensitive shortcut
749          */
750         private static void setShortcut(JMenuItem inMenuItem, String inKey)
751         {
752                 // Lookup the key in the properties
753                 String altKey = I18nManager.getText(inKey);
754                 if (altKey != null && altKey.length() == 1)
755                 {
756                         int code = altKey.charAt(0) - 'A';
757                         if (code >= 0 && code < 26)
758                         {
759                                 // Found a valid code between A and Z
760                                 inMenuItem.setAccelerator(KeyStroke.getKeyStroke(KEY_EVENTS[code],
761                                         Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
762                                 // use platform-specific key mask so Ctrl on Linux/Win, Clover on Mac
763                         }
764                 }
765         }
766
767         /**
768          * Create a JToolBar containing all toolbar buttons
769          * @return toolbar containing buttons
770          */
771         public JToolBar createToolBar()
772         {
773                 JToolBar toolbar = new JToolBar();
774                 // Add text file
775                 JButton openFileButton = new JButton(IconManager.getImageIcon(IconManager.OPEN_FILE));
776                 openFileButton.setToolTipText(I18nManager.getText("function.open"));
777                 openFileButton.addActionListener(_openFileAction);
778                 toolbar.add(openFileButton);
779                 // Add photo
780                 JButton addPhotoButton = new JButton(IconManager.getImageIcon(IconManager.ADD_PHOTO));
781                 addPhotoButton.setToolTipText(I18nManager.getText("menu.file.addphotos"));
782                 addPhotoButton.addActionListener(_addPhotoAction);
783                 toolbar.add(addPhotoButton);
784                 // Save
785                 _saveButton = new JButton(IconManager.getImageIcon(IconManager.SAVE_FILE));
786                 _saveButton.setToolTipText(I18nManager.getText("menu.file.save"));
787                 _saveButton.addActionListener(_saveAction);
788                 _saveButton.setEnabled(false);
789                 toolbar.add(_saveButton);
790                 // Undo
791                 _undoButton = new JButton(IconManager.getImageIcon(IconManager.UNDO));
792                 _undoButton.setToolTipText(I18nManager.getText("menu.track.undo"));
793                 _undoButton.addActionListener(_undoAction);
794                 _undoButton.setEnabled(false);
795                 toolbar.add(_undoButton);
796                 // Edit point
797                 _editPointButton = new JButton(IconManager.getImageIcon(IconManager.EDIT_POINT));
798                 _editPointButton.setToolTipText(I18nManager.getText("menu.point.editpoint"));
799                 _editPointButton.addActionListener(_editPointAction);
800                 _editPointButton.setEnabled(false);
801                 toolbar.add(_editPointButton);
802                 // Delete point
803                 _deletePointButton = new JButton(IconManager.getImageIcon(IconManager.DELETE_POINT));
804                 _deletePointButton.setToolTipText(I18nManager.getText("menu.point.deletepoint"));
805                 _deletePointButton.addActionListener(_deletePointAction);
806                 _deletePointButton.setEnabled(false);
807                 toolbar.add(_deletePointButton);
808                 // Delete range
809                 _deleteRangeButton = new JButton(IconManager.getImageIcon(IconManager.DELETE_RANGE));
810                 _deleteRangeButton.setToolTipText(I18nManager.getText("function.deleterange"));
811                 _deleteRangeButton.addActionListener(new ActionListener() {
812                         public void actionPerformed(ActionEvent arg0) {
813                                 FunctionLibrary.FUNCTION_DELETE_RANGE.begin();
814                         }
815                 });
816                 _deleteRangeButton.setEnabled(false);
817                 toolbar.add(_deleteRangeButton);
818                 // Cut and move
819                 _cutAndMoveButton = new JButton(IconManager.getImageIcon(IconManager.CUT_AND_MOVE));
820                 _cutAndMoveButton.setToolTipText(I18nManager.getText("menu.range.cutandmove"));
821                 _cutAndMoveButton.addActionListener(new ActionListener() {
822                         public void actionPerformed(ActionEvent arg0) {
823                                 _app.cutAndMoveSelection();
824                         }
825                 });
826                 _cutAndMoveButton.setEnabled(false);
827                 toolbar.add(_cutAndMoveButton);
828                 // Select start, end
829                 _selectStartButton = new JButton(IconManager.getImageIcon(IconManager.SET_RANGE_START));
830                 _selectStartButton.setToolTipText(I18nManager.getText("menu.range.start"));
831                 _selectStartButton.addActionListener(_selectStartAction);
832                 _selectStartButton.setEnabled(false);
833                 toolbar.add(_selectStartButton);
834                 _selectEndButton = new JButton(IconManager.getImageIcon(IconManager.SET_RANGE_END));
835                 _selectEndButton.setToolTipText(I18nManager.getText("menu.range.end"));
836                 _selectEndButton.addActionListener(_selectEndAction);
837                 _selectEndButton.setEnabled(false);
838                 toolbar.add(_selectEndButton);
839                 // Connect to point
840                 _connectButton = new JButton(IconManager.getImageIcon(IconManager.CONNECT_PHOTO));
841                 _connectButton.setToolTipText(I18nManager.getText(FunctionLibrary.FUNCTION_CONNECT_TO_POINT.getNameKey()));
842                 _connectButton.addActionListener(new ActionListener() {
843                         public void actionPerformed(ActionEvent arg0) {
844                                 FunctionLibrary.FUNCTION_CONNECT_TO_POINT.begin();
845                         }
846                 });
847                 _connectButton.setEnabled(false);
848                 toolbar.add(_connectButton);
849                 // finish off
850                 toolbar.setFloatable(false);
851                 return toolbar;
852         }
853
854
855         /**
856          * Method to update menu when file loaded
857          */
858         public void informFileLoaded()
859         {
860                 // save, undo, delete enabled
861                 _sendGpsItem.setEnabled(true);
862                 _saveItem.setEnabled(true);
863                 _undoItem.setEnabled(true);
864                 _compressItem.setEnabled(true);
865                 _deleteMarkedPointsItem.setEnabled(false);
866         }
867
868
869         /**
870          * @see tim.prune.DataSubscriber#dataUpdated(tim.prune.data.Track)
871          */
872         public void dataUpdated(byte inUpdateType)
873         {
874                 final boolean hasData = _track != null && _track.getNumPoints() > 0;
875                 final boolean hasMultiplePoints = hasData && _track.getNumPoints() > 1;
876
877                 // set functions which require data
878                 _sendGpsItem.setEnabled(hasData);
879                 _saveItem.setEnabled(hasData);
880                 _saveButton.setEnabled(hasData);
881                 _exportKmlItem.setEnabled(hasData);
882                 _exportGpxItem.setEnabled(hasData);
883                 _exportPovItem.setEnabled(hasMultiplePoints);
884                 _exportImageItem.setEnabled(hasMultiplePoints);
885                 _compressItem.setEnabled(hasData);
886                 _markRectangleItem.setEnabled(hasData);
887                 _markUphillLiftsItem.setEnabled(hasData && _track.hasAltitudeData());
888                 _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
889                 _rearrangeWaypointsItem.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
890                 final boolean hasSeveralTrackPoints = hasData && _track.hasTrackPoints() && _track.getNumPoints() > 3;
891                 _splitSegmentsItem.setEnabled(hasSeveralTrackPoints);
892                 _sewSegmentsItem.setEnabled(hasSeveralTrackPoints);
893                 _createMarkerWaypointsItem.setEnabled(hasSeveralTrackPoints);
894                 _selectAllItem.setEnabled(hasData);
895                 _selectNoneItem.setEnabled(hasData);
896                 _show3dItem.setEnabled(hasMultiplePoints);
897                 _chartItem.setEnabled(hasData);
898                 _browserMapMenu.setEnabled(hasData);
899                 _distanceItem.setEnabled(hasData);
900                 _autoplayTrack.setEnabled(hasData && _track.getNumPoints() > 3);
901                 _getGpsiesItem.setEnabled(hasData);
902                 _uploadGpsiesItem.setEnabled(hasData && _track.hasTrackPoints());
903                 _lookupSrtmItem.setEnabled(hasData);
904                 _nearbyWikipediaItem.setEnabled(hasData);
905                 _nearbyOsmPoiItem.setEnabled(hasData);
906                 _downloadOsmItem.setEnabled(hasData);
907                 _getWeatherItem.setEnabled(hasData);
908                 _findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
909                 // have we got a cache?
910                 _downloadSrtmMenu.setEnabled(hasData && Config.getConfigString(Config.KEY_DISK_CACHE) != null);
911                 // have we got any timestamps?
912                 _deleteByDateItem.setEnabled(hasData && _track.hasData(Field.TIMESTAMP));
913
914                 // is undo available?
915                 boolean hasUndo = !_app.getUndoStack().isEmpty();
916                 _undoItem.setEnabled(hasUndo);
917                 _undoButton.setEnabled(hasUndo);
918                 _clearUndoItem.setEnabled(hasUndo);
919                 // is there a current point?
920                 DataPoint currPoint = _app.getTrackInfo().getCurrentPoint();
921                 boolean hasPoint = (currPoint != null);
922                 _editPointItem.setEnabled(hasPoint);
923                 _editPointButton.setEnabled(hasPoint);
924                 _editWaypointNameItem.setEnabled(hasPoint);
925                 _deletePointItem.setEnabled(hasPoint);
926                 _deletePointButton.setEnabled(hasPoint);
927                 _selectStartItem.setEnabled(hasPoint);
928                 _selectStartButton.setEnabled(hasPoint);
929                 _selectEndItem.setEnabled(hasPoint);
930                 _selectEndButton.setEnabled(hasPoint);
931                 _duplicatePointItem.setEnabled(hasPoint);
932                 _showPeakfinderItem.setEnabled(hasPoint);
933                 _showGeohackItem.setEnabled(hasPoint);
934                 _searchOpencachingDeItem.setEnabled(hasPoint);
935                 _searchMapillaryItem.setEnabled(hasPoint);
936                 // is it a waypoint?
937                 _selectSegmentItem.setEnabled(hasPoint && !currPoint.isWaypoint());
938                 // are there any photos?
939                 boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0;
940                 _saveExifItem.setEnabled(anyPhotos && _app.getTrackInfo().getPhotoList().hasMediaWithFile());
941                 // is there a current photo, audio?
942                 Photo currentPhoto = _app.getTrackInfo().getCurrentPhoto();
943                 boolean hasPhoto = currentPhoto != null;
944                 AudioClip currentAudio = _app.getTrackInfo().getCurrentAudio();
945                 boolean hasAudio = currentAudio != null;
946                 // connect is available if (photo/audio) and point selected, and media has no point
947                 boolean connectAvailable = (hasPhoto && hasPoint && currentPhoto.getDataPoint() == null)
948                         || (hasAudio && hasPoint && currentAudio.getDataPoint() == null);
949                 _connectPhotoItem.setEnabled(hasPhoto && hasPoint && currentPhoto.getDataPoint() == null);
950                 _connectButton.setEnabled(connectAvailable);
951                 _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto.getDataPoint() != null);
952                 _correlatePhotosItem.setEnabled(anyPhotos && hasData);
953                 _rearrangePhotosItem.setEnabled(anyPhotos && hasMultiplePoints);
954                 _removePhotoItem.setEnabled(hasPhoto);
955                 _rotatePhotoLeft.setEnabled(hasPhoto);
956                 _rotatePhotoRight.setEnabled(hasPhoto);
957                 _photoPopupItem.setEnabled(hasPhoto);
958                 _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto.getExifThumbnail() != null);
959                 _selectNoPhotoItem.setEnabled(hasPhoto);
960                 boolean anyAudios = _app.getTrackInfo().getAudioList().getNumAudios() > 0;
961                 _selectNoAudioItem.setEnabled(hasAudio);
962                 _removeAudioItem.setEnabled(hasAudio);
963                 _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null);
964                 _disconnectAudioItem.setEnabled(hasAudio && currentAudio.getDataPoint() != null);
965                 _correlateAudiosItem.setEnabled(anyAudios && hasData);
966                 // is there a current range?
967                 boolean hasRange = (hasData && _selection.hasRangeSelected());
968                 _deleteRangeItem.setEnabled(hasRange);
969                 _deleteRangeButton.setEnabled(hasRange);
970                 _cropTrackItem.setEnabled(hasRange);
971                 _interpolateItem.setEnabled(hasRange);
972                 _averageItem.setEnabled(hasRange);
973                 _mergeSegmentsItem.setEnabled(hasRange);
974                 _reverseItem.setEnabled(hasRange);
975                 _addTimeOffsetItem.setEnabled(hasRange);
976                 _addAltitudeOffsetItem.setEnabled(hasRange);
977                 _removeAltitudesItem.setEnabled(hasRange);
978                 _convertNamesToTimesItem.setEnabled(hasRange && _track.hasWaypoints());
979                 _deleteFieldValuesItem.setEnabled(hasRange);
980                 _fullRangeDetailsItem.setEnabled(hasRange);
981                 _estimateTimeItem.setEnabled(hasRange);
982                 _learnEstimationParams.setEnabled(hasData && _track.hasTrackPoints() && _track.hasData(Field.TIMESTAMP)
983                         && _track.hasAltitudeData());
984                 // Is the currently selected point outside the current range?
985                 boolean canCutAndMove = hasRange && hasPoint &&
986                         (_selection.getCurrentPointIndex() < _selection.getStart()
987                         || _selection.getCurrentPointIndex() > (_selection.getEnd()+1));
988                 _cutAndMoveItem.setEnabled(canCutAndMove);
989                 _cutAndMoveButton.setEnabled(canCutAndMove);
990                 final boolean isTrackLengthTwo = hasData && _track.getNumPoints() == 2;
991                 _routingGraphHopperItem.setEnabled(isTrackLengthTwo || (hasData && hasRange));
992                 // Has the map been switched on/off?
993                 boolean mapsOn = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
994                 if (_mapCheckbox.isSelected() != mapsOn) {
995                         _mapCheckbox.setSelected(mapsOn);
996                 }
997                 // Are there any recently-used files?
998                 RecentFileList rfl = Config.getRecentFileList();
999                 final int numRecentFiles = rfl.getNumEntries();
1000                 final boolean hasRecentFiles = numRecentFiles > 0;
1001                 _recentFileMenu.setEnabled(hasRecentFiles);
1002                 if (hasRecentFiles)
1003                 {
1004                         int numItems = _recentFileMenu.getMenuComponentCount();
1005                         if (numItems == numRecentFiles)
1006                         {
1007                                 // Right number of items, just change texts
1008                                 for (int i=0; i<numRecentFiles; i++)
1009                                 {
1010                                         JMenuItem item = _recentFileMenu.getItem(i);
1011                                         RecentFile rf = rfl.getFile(i);
1012                                         item.setText(rf==null?"":rf.getFile().getName());
1013                                         item.setToolTipText(rf==null?null:rf.getFile().getAbsolutePath());
1014                                 }
1015                         }
1016                         else
1017                         {
1018                                 // Rebuild menus
1019                                 _recentFileMenu.removeAll();
1020                                 for (int i=0; i<rfl.getSize(); i++)
1021                                 {
1022                                         RecentFile rf = rfl.getFile(i);
1023                                         if (rf != null && rf.isValid())
1024                                         {
1025                                                 JMenuItem menuItem = new JMenuItem(rf.getFile().getName());
1026                                                 menuItem.setToolTipText(rf.getFile().getAbsolutePath());
1027                                                 menuItem.addActionListener(new RecentFileTrigger(_app, i));
1028                                                 _recentFileMenu.add(menuItem);
1029                                         }
1030                                 }
1031                         }
1032                 }
1033         }
1034
1035
1036         /**
1037          * Ignore action completed signals
1038          * @see tim.prune.DataSubscriber#actionCompleted(java.lang.String)
1039          */
1040         public void actionCompleted(String inMessage)
1041         {}
1042 }