]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/MenuManager.java
47044f2b568637eec671ab38355226663eda18b8
[GpsPrune.git] / 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.AudioFile;
24 import tim.prune.data.Photo;
25 import tim.prune.data.Selection;
26 import tim.prune.data.Track;
27 import tim.prune.data.TrackInfo;
28 import tim.prune.function.RearrangeWaypointsFunction.Rearrange;
29 import tim.prune.function.browser.UrlGenerator;
30
31 /**
32  * Class to manage the menu bar and tool bar,
33  * including enabling and disabling the items
34  */
35 public class MenuManager implements DataSubscriber
36 {
37         private App _app = null;
38         private Track _track = null;
39         private Selection _selection = null;
40
41         // Menu items which need enabling/disabling
42         private JMenuItem _sendGpsItem = null;
43         private JMenuItem _saveItem = null;
44         private JMenuItem _exportKmlItem = null;
45         private JMenuItem _exportGpxItem = null;
46         private JMenuItem _exportPovItem = null;
47         private JMenuItem _exportSvgItem = null;
48         private JMenuItem _undoItem = null;
49         private JMenuItem _clearUndoItem = null;
50         private JMenuItem _editPointItem = null;
51         private JMenuItem _editWaypointNameItem = null;
52         private JMenuItem _deletePointItem = null;
53         private JMenuItem _deleteRangeItem = null;
54         private JMenuItem _compressItem = null;
55         private JMenuItem _deleteMarkedPointsItem = null;
56         private JMenuItem _interpolateItem = null;
57         private JMenuItem _averageItem = null;
58         private JMenuItem _selectAllItem = null;
59         private JMenuItem _selectNoneItem = null;
60         private JMenuItem _selectStartItem = null;
61         private JMenuItem _selectEndItem = null;
62         private JMenuItem _findWaypointItem = null;
63         private JMenuItem _duplicatePointItem = null;
64         private JMenuItem _reverseItem = null;
65         private JMenuItem _addTimeOffsetItem = null;
66         private JMenuItem _addAltitudeOffsetItem = null;
67         private JMenuItem _mergeSegmentsItem = null;
68         private JMenu     _rearrangeMenu = null;
69         private JMenuItem _cutAndMoveItem = null;
70         private JMenuItem _convertNamesToTimesItem = null;
71         private JMenuItem _deleteFieldValuesItem = null;
72         private JCheckBoxMenuItem _mapCheckbox = null;
73         private JMenuItem _show3dItem = null;
74         private JMenu     _browserMapMenu = null;
75         private JMenuItem _chartItem = null;
76         private JMenuItem _getGpsiesItem = null;
77         private JMenuItem _uploadGpsiesItem = null;
78         private JMenuItem _lookupSrtmItem = null;
79         private JMenuItem _lookupWikipediaItem = null;
80         private JMenuItem _downloadOsmItem = null;
81         private JMenuItem _distanceItem = null;
82         private JMenuItem _fullRangeDetailsItem = null;
83         private JMenuItem _saveExifItem = null;
84         private JMenuItem _photoPopupItem = null;
85         private JMenuItem _selectNoPhotoItem = null;
86         private JMenuItem _connectPhotoItem = null;
87         private JMenuItem _removePhotoItem = null;
88         private JMenuItem _disconnectPhotoItem = null;
89         private JMenuItem _correlatePhotosItem = null;
90         private JMenuItem _rearrangePhotosItem = null;
91         private JMenuItem _rotatePhotoLeft = null;
92         private JMenuItem _rotatePhotoRight = null;
93         private JMenuItem _ignoreExifThumb = null;
94         private JMenuItem _connectAudioItem = null;
95         private JMenuItem _disconnectAudioItem = null;
96         private JMenuItem _removeAudioItem = null;
97         private JMenuItem _correlateAudiosItem = null;
98         private JMenuItem _selectNoAudioItem = null;
99         private JCheckBoxMenuItem _onlineCheckbox = null;
100
101         // ActionListeners for reuse by menu and toolbar
102         private ActionListener _openFileAction = null;
103         private ActionListener _addPhotoAction = null;
104         private ActionListener _saveAction = null;
105         private ActionListener _undoAction = null;
106         private ActionListener _editPointAction = null;
107         private ActionListener _deletePointAction = null;
108         private ActionListener _deleteRangeAction = null;
109         private ActionListener _selectStartAction = null;
110         private ActionListener _selectEndAction = null;
111
112         // Toolbar buttons which need enabling/disabling
113         private JButton _saveButton = null;
114         private JButton _undoButton = null;
115         private JButton _editPointButton = null;
116         private JButton _deletePointButton = null;
117         private JButton _deleteRangeButton = null;
118         private JButton _selectStartButton = null;
119         private JButton _selectEndButton = null;
120         private JButton _connectButton = null;
121
122         /** Array of key events */
123         private static final int[] KEY_EVENTS = {
124                 KeyEvent.VK_A, KeyEvent.VK_B, KeyEvent.VK_C, KeyEvent.VK_D, KeyEvent.VK_E,
125                 KeyEvent.VK_F, KeyEvent.VK_G, KeyEvent.VK_H, KeyEvent.VK_I, KeyEvent.VK_J,
126                 KeyEvent.VK_K, KeyEvent.VK_L, KeyEvent.VK_M, KeyEvent.VK_N, KeyEvent.VK_O,
127                 KeyEvent.VK_P, KeyEvent.VK_Q, KeyEvent.VK_R, KeyEvent.VK_S, KeyEvent.VK_T,
128                 KeyEvent.VK_U, KeyEvent.VK_V, KeyEvent.VK_W, KeyEvent.VK_X, KeyEvent.VK_Y, KeyEvent.VK_Z};
129
130
131         /**
132          * Constructor
133          * @param inApp application to call on menu actions
134          * @param inTrackInfo track info object
135          */
136         public MenuManager(App inApp, TrackInfo inTrackInfo)
137         {
138                 _app = inApp;
139                 _track = inTrackInfo.getTrack();
140                 _selection = inTrackInfo.getSelection();
141         }
142
143
144         /**
145          * Create a JMenuBar containing all menu items
146          * @return JMenuBar
147          */
148         public JMenuBar createMenuBar()
149         {
150                 JMenuBar menubar = new JMenuBar();
151                 JMenu fileMenu = new JMenu(I18nManager.getText("menu.file"));
152                 setAltKey(fileMenu, "altkey.menu.file");
153                 // Open file
154                 JMenuItem openMenuItem = new JMenuItem(I18nManager.getText("function.open"));
155                 setShortcut(openMenuItem, "shortcut.menu.file.open");
156                 _openFileAction = new ActionListener() {
157                         public void actionPerformed(ActionEvent e) {
158                                 _app.openFile();
159                         }
160                 };
161                 openMenuItem.addActionListener(_openFileAction);
162                 fileMenu.add(openMenuItem);
163                 // Add photos
164                 JMenuItem addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos"));
165                 _addPhotoAction = new ActionListener() {
166                         public void actionPerformed(ActionEvent e) {
167                                 _app.addPhotos();
168                         }
169                 };
170                 addPhotosMenuItem.addActionListener(_addPhotoAction);
171                 fileMenu.add(addPhotosMenuItem);
172                 // Add audio files
173                 JMenuItem addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
174                 fileMenu.add(addAudioMenuItem);
175                 fileMenu.addSeparator();
176                 // Load from GPS
177                 JMenuItem loadFromGpsMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSLOAD);
178                 setShortcut(loadFromGpsMenuItem, "shortcut.menu.file.load");
179                 fileMenu.add(loadFromGpsMenuItem);
180                 // Send to GPS
181                 _sendGpsItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSSAVE, false);
182                 fileMenu.add(_sendGpsItem);
183                 fileMenu.addSeparator();
184                 // Save
185                 _saveItem = new JMenuItem(I18nManager.getText("menu.file.save"), KeyEvent.VK_S);
186                 setShortcut(_saveItem, "shortcut.menu.file.save");
187                 _saveAction = new ActionListener() {
188                         public void actionPerformed(ActionEvent e) {
189                                 _app.saveFile();
190                         }
191                 };
192                 _saveItem.addActionListener(_saveAction);
193                 _saveItem.setEnabled(false);
194                 fileMenu.add(_saveItem);
195                 // Export - Kml
196                 _exportKmlItem = makeMenuItem(FunctionLibrary.FUNCTION_KMLEXPORT, false);
197                 fileMenu.add(_exportKmlItem);
198                 // Gpx
199                 _exportGpxItem = makeMenuItem(FunctionLibrary.FUNCTION_GPXEXPORT, false);
200                 fileMenu.add(_exportGpxItem);
201                 // Pov
202                 _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT, false);
203                 fileMenu.add(_exportPovItem);
204                 // Svg
205                 _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT, false);
206                 fileMenu.add(_exportSvgItem);
207                 fileMenu.addSeparator();
208                 JMenuItem exitMenuItem = new JMenuItem(I18nManager.getText("menu.file.exit"));
209                 exitMenuItem.addActionListener(new ActionListener() {
210                         public void actionPerformed(ActionEvent e) {
211                                 _app.exit();
212                         }
213                 });
214                 fileMenu.add(exitMenuItem);
215                 menubar.add(fileMenu);
216                 // Track menu
217                 JMenu trackMenu = new JMenu(I18nManager.getText("menu.track"));
218                 setAltKey(trackMenu, "altkey.menu.track");
219                 _undoItem = new JMenuItem(I18nManager.getText("menu.track.undo"));
220                 setShortcut(_undoItem, "shortcut.menu.track.undo");
221                 _undoAction = new ActionListener() {
222                         public void actionPerformed(ActionEvent e) {
223                                 _app.beginUndo();
224                         }
225                 };
226                 _undoItem.addActionListener(_undoAction);
227                 _undoItem.setEnabled(false);
228                 trackMenu.add(_undoItem);
229                 _clearUndoItem = new JMenuItem(I18nManager.getText("menu.track.clearundo"));
230                 _clearUndoItem.addActionListener(new ActionListener() {
231                         public void actionPerformed(ActionEvent e) {
232                                 _app.clearUndo();
233                         }
234                 });
235                 _clearUndoItem.setEnabled(false);
236                 trackMenu.add(_clearUndoItem);
237                 trackMenu.addSeparator();
238                 _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS, false);
239                 setShortcut(_compressItem, "shortcut.menu.edit.compress");
240                 trackMenu.add(_compressItem);
241                 _deleteMarkedPointsItem = new JMenuItem(I18nManager.getText("menu.track.deletemarked"));
242                 _deleteMarkedPointsItem.addActionListener(new ActionListener() {
243                         public void actionPerformed(ActionEvent e) {
244                                 _app.finishCompressTrack();
245                         }
246                 });
247                 _deleteMarkedPointsItem.setEnabled(false);
248                 trackMenu.add(_deleteMarkedPointsItem);
249                 trackMenu.addSeparator();
250                 // Rearrange waypoints
251                 _rearrangeMenu = new JMenu(I18nManager.getText("menu.track.rearrange"));
252                 _rearrangeMenu.setEnabled(false);
253                 JMenuItem  rearrangeStartItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.start"));
254                 rearrangeStartItem.addActionListener(new ActionListener() {
255                         public void actionPerformed(ActionEvent e) {
256                                 FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_START);
257                         }
258                 });
259                 rearrangeStartItem.setEnabled(true);
260                 _rearrangeMenu.add(rearrangeStartItem);
261                 JMenuItem rearrangeEndItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.end"));
262                 rearrangeEndItem.addActionListener(new ActionListener() {
263                         public void actionPerformed(ActionEvent e) {
264                                 FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_END);
265                         }
266                 });
267                 rearrangeEndItem.setEnabled(true);
268                 _rearrangeMenu.add(rearrangeEndItem);
269                 JMenuItem rearrangeNearestItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.nearest"));
270                 rearrangeNearestItem.addActionListener(new ActionListener() {
271                         public void actionPerformed(ActionEvent e) {
272                                 FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_NEAREST);
273                         }
274                 });
275                 rearrangeNearestItem.setEnabled(true);
276                 _rearrangeMenu.add(rearrangeNearestItem);
277                 trackMenu.add(_rearrangeMenu);
278                 // Get gpsies tracks
279                 _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
280                 trackMenu.add(_getGpsiesItem);
281                 // Upload to gpsies
282                 _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
283                 trackMenu.add(_uploadGpsiesItem);
284                 _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
285                 trackMenu.add(_lookupSrtmItem);
286                 _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false);
287                 trackMenu.add(_lookupWikipediaItem);
288                 JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
289                 trackMenu.add(searchWikipediaNamesItem);
290                 _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
291                 trackMenu.add(_downloadOsmItem);
292                 menubar.add(trackMenu);
293
294                 // Range menu
295                 JMenu rangeMenu = new JMenu(I18nManager.getText("menu.range"));
296                 setAltKey(rangeMenu, "altkey.menu.range");
297                 _selectAllItem = new JMenuItem(I18nManager.getText("menu.range.all"));
298                 setShortcut(_selectAllItem, "shortcut.menu.range.all");
299                 _selectAllItem.setEnabled(false);
300                 _selectAllItem.addActionListener(new ActionListener() {
301                         public void actionPerformed(ActionEvent e) {
302                                 _selection.selectRange(0, _track.getNumPoints()-1);
303                         }
304                 });
305                 rangeMenu.add(_selectAllItem);
306                 _selectNoneItem = new JMenuItem(I18nManager.getText("menu.range.none"));
307                 _selectNoneItem.setEnabled(false);
308                 _selectNoneItem.addActionListener(new ActionListener() {
309                         public void actionPerformed(ActionEvent e) {
310                                 _app.selectNone();
311                         }
312                 });
313                 rangeMenu.add(_selectNoneItem);
314                 rangeMenu.addSeparator();
315                 _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start"));
316                 _selectStartItem.setEnabled(false);
317                 _selectStartAction = new ActionListener() {
318                         public void actionPerformed(ActionEvent e) {
319                                 _selection.selectRangeStart();
320                         }
321                 };
322                 _selectStartItem.addActionListener(_selectStartAction);
323                 rangeMenu.add(_selectStartItem);
324                 _selectEndItem = new JMenuItem(I18nManager.getText("menu.range.end"));
325                 _selectEndItem.setEnabled(false);
326                 _selectEndAction = new ActionListener() {
327                         public void actionPerformed(ActionEvent e) {
328                                 _selection.selectRangeEnd();
329                         }
330                 };
331                 _selectEndItem.addActionListener(_selectEndAction);
332                 rangeMenu.add(_selectEndItem);
333                 rangeMenu.addSeparator();
334                 _deleteRangeItem = new JMenuItem(I18nManager.getText("menu.range.deleterange"));
335                 _deleteRangeAction = new ActionListener() {
336                         public void actionPerformed(ActionEvent e) {
337                                 _app.deleteSelectedRange();
338                         }
339                 };
340                 _deleteRangeItem.addActionListener(_deleteRangeAction);
341                 _deleteRangeItem.setEnabled(false);
342                 rangeMenu.add(_deleteRangeItem);
343                 _reverseItem = new JMenuItem(I18nManager.getText("menu.range.reverse"));
344                 _reverseItem.addActionListener(new ActionListener() {
345                         public void actionPerformed(ActionEvent e) {
346                                 _app.reverseRange();
347                         }
348                 });
349                 _reverseItem.setEnabled(false);
350                 rangeMenu.add(_reverseItem);
351                 _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET, false);
352                 rangeMenu.add(_addTimeOffsetItem);
353                 _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET, false);
354                 rangeMenu.add(_addAltitudeOffsetItem);
355                 _mergeSegmentsItem = new JMenuItem(I18nManager.getText("menu.range.mergetracksegments"));
356                 _mergeSegmentsItem.addActionListener(new ActionListener() {
357                         public void actionPerformed(ActionEvent e) {
358                                 _app.mergeTrackSegments();
359                         }
360                 });
361                 _mergeSegmentsItem.setEnabled(false);
362                 rangeMenu.add(_mergeSegmentsItem);
363                 _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false);
364                 rangeMenu.add(_deleteFieldValuesItem);
365                 rangeMenu.addSeparator();
366                 _interpolateItem = new JMenuItem(I18nManager.getText("menu.range.interpolate"));
367                 _interpolateItem.addActionListener(new ActionListener() {
368                         public void actionPerformed(ActionEvent e) {
369                                 _app.interpolateSelection();
370                         }
371                 });
372                 _interpolateItem.setEnabled(false);
373                 rangeMenu.add(_interpolateItem);
374                 _averageItem = new JMenuItem(I18nManager.getText("menu.range.average"));
375                 _averageItem.addActionListener(new ActionListener() {
376                         public void actionPerformed(ActionEvent e) {
377                                 _app.averageSelection();
378                         }
379                 });
380                 _averageItem.setEnabled(false);
381                 rangeMenu.add(_averageItem);
382                 _cutAndMoveItem = new JMenuItem(I18nManager.getText("menu.range.cutandmove"));
383                 _cutAndMoveItem.addActionListener(new ActionListener() {
384                         public void actionPerformed(ActionEvent e)
385                         {
386                                 _app.cutAndMoveSelection();
387                         }
388                 });
389                 _cutAndMoveItem.setEnabled(false);
390                 rangeMenu.add(_cutAndMoveItem);
391                 _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES, false);
392                 rangeMenu.add(_convertNamesToTimesItem);
393                 menubar.add(rangeMenu);
394
395                 // Point menu
396                 JMenu pointMenu = new JMenu(I18nManager.getText("menu.point"));
397                 setAltKey(pointMenu, "altkey.menu.point");
398                 _editPointItem = new JMenuItem(I18nManager.getText("menu.point.editpoint"));
399                 _editPointAction = new ActionListener() {
400                         public void actionPerformed(ActionEvent e) {
401                                 _app.editCurrentPoint();
402                         }
403                 };
404                 _editPointItem.addActionListener(_editPointAction);
405                 _editPointItem.setEnabled(false);
406                 pointMenu.add(_editPointItem);
407                 _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME, false);
408                 pointMenu.add(_editWaypointNameItem);
409                 _deletePointItem = new JMenuItem(I18nManager.getText("menu.point.deletepoint"));
410                 _deletePointAction = new ActionListener() {
411                         public void actionPerformed(ActionEvent e) {
412                                 _app.deleteCurrentPoint();
413                         }
414                 };
415                 _deletePointItem.addActionListener(_deletePointAction);
416                 _deletePointItem.setEnabled(false);
417                 _deletePointItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
418                 pointMenu.add(_deletePointItem);
419                 pointMenu.addSeparator();
420                 // find a waypoint
421                 _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT, false);
422                 pointMenu.add(_findWaypointItem);
423                 // duplicate current point
424                 _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT, false);
425                 pointMenu.add(_duplicatePointItem);
426                 // paste coordinates function
427                 JMenuItem pasteCoordsItem = makeMenuItem(FunctionLibrary.FUNCTION_PASTE_COORDINATES);
428                 pointMenu.add(pasteCoordsItem);
429                 menubar.add(pointMenu);
430
431                 // Add view menu
432                 JMenu viewMenu = new JMenu(I18nManager.getText("menu.view"));
433                 setAltKey(viewMenu, "altkey.menu.view");
434                 // Turn map display on/off
435                 _mapCheckbox = new JCheckBoxMenuItem(
436                         I18nManager.getText("menu.map.showmap"), false);
437                 _mapCheckbox.addActionListener(new ActionListener() {
438                         public void actionPerformed(ActionEvent e) {
439                                 Config.setConfigBoolean(Config.KEY_SHOW_MAP, _mapCheckbox.isSelected());
440                                 UpdateMessageBroker.informSubscribers();
441                         }
442                 });
443                 viewMenu.add(_mapCheckbox);
444                 // Turn off the sidebars
445                 JCheckBoxMenuItem sidebarsCheckbox = new JCheckBoxMenuItem(I18nManager.getText("menu.view.showsidebars"));
446                 sidebarsCheckbox.setSelected(true);
447                 sidebarsCheckbox.addActionListener(new ActionListener() {
448                         public void actionPerformed(ActionEvent e) {
449                                 _app.toggleSidebars();
450                         }
451                 });
452                 viewMenu.add(sidebarsCheckbox);
453                 // 3d
454                 _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D, false);
455                 viewMenu.add(_show3dItem);
456                 // browser submenu
457                 _browserMapMenu = new JMenu(I18nManager.getText("menu.view.browser"));
458                 _browserMapMenu.setEnabled(false);
459                 JMenuItem googleMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.google"));
460                 googleMapsItem.addActionListener(new ActionListener() {
461                         public void actionPerformed(ActionEvent e) {
462                                 _app.showExternalMap(UrlGenerator.MAP_SOURCE_GOOGLE);
463                         }
464                 });
465                 _browserMapMenu.add(googleMapsItem);
466                 JMenuItem openMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.openstreetmap"));
467                 openMapsItem.addActionListener(new ActionListener() {
468                         public void actionPerformed(ActionEvent e) {
469                                 _app.showExternalMap(UrlGenerator.MAP_SOURCE_OSM);
470                         }
471                 });
472                 _browserMapMenu.add(openMapsItem);
473                 JMenuItem mapquestMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.mapquest"));
474                 mapquestMapsItem.addActionListener(new ActionListener() {
475                         public void actionPerformed(ActionEvent e) {
476                                 _app.showExternalMap(UrlGenerator.MAP_SOURCE_MAPQUEST);
477                         }
478                 });
479                 _browserMapMenu.add(mapquestMapsItem);
480                 JMenuItem yahooMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.yahoo"));
481                 yahooMapsItem.addActionListener(new ActionListener() {
482                         public void actionPerformed(ActionEvent e) {
483                                 _app.showExternalMap(UrlGenerator.MAP_SOURCE_YAHOO);
484                         }
485                 });
486                 _browserMapMenu.add(yahooMapsItem);
487                 JMenuItem bingMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.bing"));
488                 bingMapsItem.addActionListener(new ActionListener() {
489                         public void actionPerformed(ActionEvent e) {
490                                 _app.showExternalMap(UrlGenerator.MAP_SOURCE_BING);
491                         }
492                 });
493                 _browserMapMenu.add(bingMapsItem);
494                 viewMenu.add(_browserMapMenu);
495                 // Charts
496                 _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS, false);
497                 viewMenu.add(_chartItem);
498                 // Distances
499                 _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES, false);
500                 viewMenu.add(_distanceItem);
501                 // full range details
502                 _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS, false);
503                 viewMenu.add(_fullRangeDetailsItem);
504                 menubar.add(viewMenu);
505
506                 // Add photo menu
507                 JMenu photoMenu = new JMenu(I18nManager.getText("menu.photo"));
508                 setAltKey(photoMenu, "altkey.menu.photo");
509                 addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos"));
510                 addPhotosMenuItem.addActionListener(_addPhotoAction);
511                 photoMenu.add(addPhotosMenuItem);
512                 _saveExifItem = new JMenuItem(I18nManager.getText("menu.photo.saveexif"));
513                 _saveExifItem.addActionListener(new ActionListener() {
514                         public void actionPerformed(ActionEvent e) {
515                                 _app.saveExif();
516                         }
517                 });
518                 _saveExifItem.setEnabled(false);
519                 photoMenu.add(_saveExifItem);
520                 // Deselect current photo
521                 _selectNoPhotoItem = new JMenuItem(I18nManager.getText("menu.range.none"));
522                 _selectNoPhotoItem.setEnabled(false);
523                 _selectNoPhotoItem.addActionListener(new ActionListener() {
524                         public void actionPerformed(ActionEvent e) {
525                                 _app.getTrackInfo().selectPhoto(-1);
526                         }
527                 });
528                 photoMenu.add(_selectNoPhotoItem);
529                 photoMenu.addSeparator();
530                 _connectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
531                 photoMenu.add(_connectPhotoItem);
532                 // disconnect photo
533                 _disconnectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_PHOTO, false);
534                 photoMenu.add(_disconnectPhotoItem);
535                 _removePhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_PHOTO, false);
536                 photoMenu.add(_removePhotoItem);
537                 // Rotate current photo
538                 _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT, false);
539                 photoMenu.add(_rotatePhotoLeft);
540                 _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT, false);
541                 photoMenu.add(_rotatePhotoRight);
542                 // Show photo popup
543                 _photoPopupItem = makeMenuItem(FunctionLibrary.FUNCTION_PHOTO_POPUP, false);
544                 photoMenu.add(_photoPopupItem);
545                 _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB, false);
546                 photoMenu.add(_ignoreExifThumb);
547                 photoMenu.addSeparator();
548                 // correlate all photos
549                 _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS, false);
550                 photoMenu.add(_correlatePhotosItem);
551                 // rearrange photo points
552                 _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS, false);
553                 photoMenu.add(_rearrangePhotosItem);
554                 menubar.add(photoMenu);
555
556                 // Audio menu
557                 JMenu audioMenu = new JMenu(I18nManager.getText("menu.audio"));
558                 setAltKey(audioMenu, "altkey.menu.audio");
559                 addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
560                 audioMenu.add(addAudioMenuItem);
561                 _selectNoAudioItem = new JMenuItem(I18nManager.getText("menu.range.none"));
562                 _selectNoAudioItem.setEnabled(false);
563                 _selectNoAudioItem.addActionListener(new ActionListener() {
564                         public void actionPerformed(ActionEvent e) {
565                                 _app.getTrackInfo().selectAudio(-1);
566                         }
567                 });
568                 audioMenu.add(_selectNoAudioItem);
569                 audioMenu.addSeparator();
570                 // connect audio
571                 _connectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
572                 audioMenu.add(_connectAudioItem);
573                 // Disconnect current audio file
574                 _disconnectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_AUDIO, false);
575                 audioMenu.add(_disconnectAudioItem);
576                 // Remove current audio file
577                 _removeAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_AUDIO, false);
578                 audioMenu.add(_removeAudioItem);
579                 audioMenu.addSeparator();
580                 // Correlate audio files
581                 _correlateAudiosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_AUDIOS, false);
582                 audioMenu.add(_correlateAudiosItem);
583                 menubar.add(audioMenu);
584
585                 // Settings menu
586                 JMenu settingsMenu = new JMenu(I18nManager.getText("menu.settings"));
587                 setAltKey(settingsMenu, "altkey.menu.settings");
588                 // Set the map background
589                 JMenuItem mapBgItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_MAP_BG);
590                 settingsMenu.add(mapBgItem);
591                 _onlineCheckbox = new JCheckBoxMenuItem(I18nManager.getText("menu.settings.onlinemode"));
592                 _onlineCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_ONLINE_MODE));
593                 _onlineCheckbox.addActionListener(new ActionListener() {
594                         public void actionPerformed(ActionEvent e) {
595                                 boolean isOnline = _onlineCheckbox.isSelected();
596                                 Config.setConfigBoolean(Config.KEY_ONLINE_MODE, isOnline);
597                                 if (isOnline) {UpdateMessageBroker.informSubscribers();}
598                         }
599                 });
600                 settingsMenu.add(_onlineCheckbox);
601                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_DISK_CACHE));
602                 settingsMenu.addSeparator();
603                 // Set kmz image size
604                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_KMZ_IMAGE_SIZE));
605                 // Set program paths
606                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_PATHS));
607                 // Set colours
608                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS));
609                 // Set line width used for drawing
610                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LINE_WIDTH));
611                 // Set language
612                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE));
613                 settingsMenu.addSeparator();
614                 // Save configuration
615                 settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG));
616                 menubar.add(settingsMenu);
617
618                 // Help menu
619                 JMenu helpMenu = new JMenu(I18nManager.getText("menu.help"));
620                 setAltKey(helpMenu, "altkey.menu.help");
621                 JMenuItem helpItem = makeMenuItem(FunctionLibrary.FUNCTION_HELP);
622                 setShortcut(helpItem, "shortcut.menu.help.help");
623                 helpMenu.add(helpItem);
624                 helpMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SHOW_KEYS));
625                 helpMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_ABOUT));
626                 helpMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_CHECK_VERSION));
627                 menubar.add(helpMenu);
628
629                 return menubar;
630         }
631
632         /**
633          * Convenience method for making a menu item using a function
634          * @param inFunction function
635          * @param inEnabled flag to specify initial enabled state
636          * @return menu item using localized name of function
637          */
638         private static JMenuItem makeMenuItem(GenericFunction inFunction, boolean inEnabled)
639         {
640                 JMenuItem item = makeMenuItem(inFunction);
641                 item.setEnabled(inEnabled);
642                 return item;
643         }
644
645         /**
646          * Convenience method for making a menu item using a function
647          * @param inFunction function
648          * @return menu item using localized name of function
649          */
650         private static JMenuItem makeMenuItem(GenericFunction inFunction)
651         {
652                 JMenuItem item = new JMenuItem(I18nManager.getText(inFunction.getNameKey()));
653                 item.addActionListener(new FunctionLauncher(inFunction));
654                 return item;
655         }
656
657         /**
658          * Set the alt key for the given menu
659          * @param inMenu menu to set
660          * @param inKey key to lookup to get language-sensitive altkey
661          */
662         private static void setAltKey(JMenu inMenu, String inKey)
663         {
664                 // Lookup the key in the properties
665                 String altKey = I18nManager.getText(inKey);
666                 if (altKey != null && altKey.length() == 1)
667                 {
668                         int code = altKey.charAt(0) - 'A';
669                         if (code >= 0 && code < 26)
670                         {
671                                 // Found a valid code between A and Z
672                                 inMenu.setMnemonic(KEY_EVENTS[code]);
673                         }
674                 }
675         }
676
677         /**
678          * Set the shortcut key for the given menu item
679          * @param inMenuItem menu item to set
680          * @param inKey key to lookup to get language-sensitive shortcut
681          */
682         private static void setShortcut(JMenuItem inMenuItem, String inKey)
683         {
684                 // Lookup the key in the properties
685                 String altKey = I18nManager.getText(inKey);
686                 if (altKey != null && altKey.length() == 1)
687                 {
688                         int code = altKey.charAt(0) - 'A';
689                         if (code >= 0 && code < 26)
690                         {
691                                 // Found a valid code between A and Z
692                                 inMenuItem.setAccelerator(KeyStroke.getKeyStroke(KEY_EVENTS[code],
693                                         Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
694                                 // use platform-specific key mask so Ctrl on Linux/Win, Clover on Mac
695                         }
696                 }
697         }
698
699         /**
700          * Create a JToolBar containing all toolbar buttons
701          * @return toolbar containing buttons
702          */
703         public JToolBar createToolBar()
704         {
705                 JToolBar toolbar = new JToolBar();
706                 // Add text file
707                 JButton openFileButton = new JButton(IconManager.getImageIcon(IconManager.OPEN_FILE));
708                 openFileButton.setToolTipText(I18nManager.getText("function.open"));
709                 openFileButton.addActionListener(_openFileAction);
710                 toolbar.add(openFileButton);
711                 // Add photo
712                 JButton addPhotoButton = new JButton(IconManager.getImageIcon(IconManager.ADD_PHOTO));
713                 addPhotoButton.setToolTipText(I18nManager.getText("menu.file.addphotos"));
714                 addPhotoButton.addActionListener(_addPhotoAction);
715                 toolbar.add(addPhotoButton);
716                 // Save
717                 _saveButton = new JButton(IconManager.getImageIcon(IconManager.SAVE_FILE));
718                 _saveButton.setToolTipText(I18nManager.getText("menu.file.save"));
719                 _saveButton.addActionListener(_saveAction);
720                 _saveButton.setEnabled(false);
721                 toolbar.add(_saveButton);
722                 // Undo
723                 _undoButton = new JButton(IconManager.getImageIcon(IconManager.UNDO));
724                 _undoButton.setToolTipText(I18nManager.getText("menu.track.undo"));
725                 _undoButton.addActionListener(_undoAction);
726                 _undoButton.setEnabled(false);
727                 toolbar.add(_undoButton);
728                 // Edit point
729                 _editPointButton = new JButton(IconManager.getImageIcon(IconManager.EDIT_POINT));
730                 _editPointButton.setToolTipText(I18nManager.getText("menu.point.editpoint"));
731                 _editPointButton.addActionListener(_editPointAction);
732                 _editPointButton.setEnabled(false);
733                 toolbar.add(_editPointButton);
734                 // Delete point
735                 _deletePointButton = new JButton(IconManager.getImageIcon(IconManager.DELETE_POINT));
736                 _deletePointButton.setToolTipText(I18nManager.getText("menu.point.deletepoint"));
737                 _deletePointButton.addActionListener(_deletePointAction);
738                 _deletePointButton.setEnabled(false);
739                 toolbar.add(_deletePointButton);
740                 // Delete range
741                 _deleteRangeButton = new JButton(IconManager.getImageIcon(IconManager.DELETE_RANGE));
742                 _deleteRangeButton.setToolTipText(I18nManager.getText("menu.range.deleterange"));
743                 _deleteRangeButton.addActionListener(_deleteRangeAction);
744                 _deleteRangeButton.setEnabled(false);
745                 toolbar.add(_deleteRangeButton);
746                 // Select start, end
747                 _selectStartButton = new JButton(IconManager.getImageIcon(IconManager.SET_RANGE_START));
748                 _selectStartButton.setToolTipText(I18nManager.getText("menu.range.start"));
749                 _selectStartButton.addActionListener(_selectStartAction);
750                 _selectStartButton.setEnabled(false);
751                 toolbar.add(_selectStartButton);
752                 _selectEndButton = new JButton(IconManager.getImageIcon(IconManager.SET_RANGE_END));
753                 _selectEndButton.setToolTipText(I18nManager.getText("menu.range.end"));
754                 _selectEndButton.addActionListener(_selectEndAction);
755                 _selectEndButton.setEnabled(false);
756                 toolbar.add(_selectEndButton);
757                 // Connect to point
758                 _connectButton = new JButton(IconManager.getImageIcon(IconManager.CONNECT_PHOTO));
759                 _connectButton.setToolTipText(I18nManager.getText(FunctionLibrary.FUNCTION_CONNECT_TO_POINT.getNameKey()));
760                 _connectButton.addActionListener(new ActionListener() {
761                         public void actionPerformed(ActionEvent arg0) {
762                                 FunctionLibrary.FUNCTION_CONNECT_TO_POINT.begin();
763                         }
764                 });
765                 _connectButton.setEnabled(false);
766                 toolbar.add(_connectButton);
767                 // finish off
768                 toolbar.setFloatable(false);
769                 return toolbar;
770         }
771
772
773         /**
774          * Method to update menu when file loaded
775          */
776         public void informFileLoaded()
777         {
778                 // save, undo, delete enabled
779                 _sendGpsItem.setEnabled(true);
780                 _saveItem.setEnabled(true);
781                 _undoItem.setEnabled(true);
782                 _compressItem.setEnabled(true);
783                 _deleteMarkedPointsItem.setEnabled(false);
784         }
785
786
787         /**
788          * @see tim.prune.DataSubscriber#dataUpdated(tim.prune.data.Track)
789          */
790         public void dataUpdated(byte inUpdateType)
791         {
792                 boolean hasData = (_track != null && _track.getNumPoints() > 0);
793                 // set functions which require data
794                 _sendGpsItem.setEnabled(hasData);
795                 _saveItem.setEnabled(hasData);
796                 _saveButton.setEnabled(hasData);
797                 _exportKmlItem.setEnabled(hasData);
798                 _exportGpxItem.setEnabled(hasData);
799                 _exportPovItem.setEnabled(hasData);
800                 _exportSvgItem.setEnabled(hasData);
801                 _compressItem.setEnabled(hasData);
802                 _deleteMarkedPointsItem.setEnabled(hasData && _track.hasMarkedPoints());
803                 _rearrangeMenu.setEnabled(hasData && _track.hasTrackPoints() && _track.hasWaypoints());
804                 _selectAllItem.setEnabled(hasData);
805                 _selectNoneItem.setEnabled(hasData);
806                 _show3dItem.setEnabled(hasData);
807                 _chartItem.setEnabled(hasData);
808                 _browserMapMenu.setEnabled(hasData);
809                 _distanceItem.setEnabled(hasData);
810                 _getGpsiesItem.setEnabled(hasData);
811                 _uploadGpsiesItem.setEnabled(hasData && _track.hasTrackPoints());
812                 _lookupSrtmItem.setEnabled(hasData);
813                 _lookupWikipediaItem.setEnabled(hasData);
814                 _downloadOsmItem.setEnabled(hasData);
815                 _findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
816                 // is undo available?
817                 boolean hasUndo = !_app.getUndoStack().isEmpty();
818                 _undoItem.setEnabled(hasUndo);
819                 _undoButton.setEnabled(hasUndo);
820                 _clearUndoItem.setEnabled(hasUndo);
821                 // is there a current point?
822                 boolean hasPoint = (hasData && _selection.getCurrentPointIndex() >= 0);
823                 _editPointItem.setEnabled(hasPoint);
824                 _editPointButton.setEnabled(hasPoint);
825                 _editWaypointNameItem.setEnabled(hasPoint);
826                 _deletePointItem.setEnabled(hasPoint);
827                 _deletePointButton.setEnabled(hasPoint);
828                 _selectStartItem.setEnabled(hasPoint);
829                 _selectStartButton.setEnabled(hasPoint);
830                 _selectEndItem.setEnabled(hasPoint);
831                 _selectEndButton.setEnabled(hasPoint);
832                 _duplicatePointItem.setEnabled(hasPoint);
833                 // are there any photos?
834                 boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0;
835                 _saveExifItem.setEnabled(anyPhotos);
836                 // is there a current photo, audio?
837                 Photo currentPhoto = _app.getTrackInfo().getCurrentPhoto();
838                 boolean hasPhoto = currentPhoto != null;
839                 AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio();
840                 boolean hasAudio = currentAudio != null;
841                 // connect is available if (photo/audio) and point selected, and media has no point
842                 boolean connectAvailable = (hasPhoto && hasPoint && currentPhoto.getDataPoint() == null)
843                         || (hasAudio && hasPoint && currentAudio.getDataPoint() == null);
844                 _connectPhotoItem.setEnabled(hasPhoto && hasPoint && currentPhoto.getDataPoint() == null);
845                 _connectButton.setEnabled(connectAvailable);
846                 _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto.getDataPoint() != null);
847                 _correlatePhotosItem.setEnabled(anyPhotos && hasData);
848                 _rearrangePhotosItem.setEnabled(anyPhotos && hasData && _track.getNumPoints() > 1);
849                 _removePhotoItem.setEnabled(hasPhoto);
850                 _rotatePhotoLeft.setEnabled(hasPhoto);
851                 _rotatePhotoRight.setEnabled(hasPhoto);
852                 _photoPopupItem.setEnabled(hasPhoto);
853                 _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto.getExifThumbnail() != null);
854                 _selectNoPhotoItem.setEnabled(hasPhoto);
855                 boolean anyAudios = _app.getTrackInfo().getAudioList().getNumAudios() > 0;
856                 _selectNoAudioItem.setEnabled(hasAudio);
857                 _removeAudioItem.setEnabled(hasAudio);
858                 _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null);
859                 _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null);
860                 _correlateAudiosItem.setEnabled(anyAudios && hasData);
861                 // is there a current range?
862                 boolean hasRange = (hasData && _selection.hasRangeSelected());
863                 _deleteRangeItem.setEnabled(hasRange);
864                 _deleteRangeButton.setEnabled(hasRange);
865                 _interpolateItem.setEnabled(hasRange
866                         && (_selection.getEnd() - _selection.getStart()) == 1);
867                 _averageItem.setEnabled(hasRange);
868                 _mergeSegmentsItem.setEnabled(hasRange);
869                 _reverseItem.setEnabled(hasRange);
870                 _addTimeOffsetItem.setEnabled(hasRange);
871                 _addAltitudeOffsetItem.setEnabled(hasRange);
872                 _convertNamesToTimesItem.setEnabled(hasRange && _track.hasWaypoints());
873                 _deleteFieldValuesItem.setEnabled(hasRange);
874                 _fullRangeDetailsItem.setEnabled(hasRange);
875                 // Is the currently selected point outside the current range?
876                 _cutAndMoveItem.setEnabled(hasRange && hasPoint &&
877                         (_selection.getCurrentPointIndex() < _selection.getStart()
878                                 || _selection.getCurrentPointIndex() > (_selection.getEnd()+1)));
879                 // Has the map been switched on/off?
880                 boolean mapsOn = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
881                 if (_mapCheckbox.isSelected() != mapsOn) {
882                         _mapCheckbox.setSelected(mapsOn);
883                 }
884         }
885
886
887         /**
888          * Ignore action completed signals
889          * @see tim.prune.DataSubscriber#actionCompleted(java.lang.String)
890          */
891         public void actionCompleted(String inMessage)
892         {}
893 }