Use the canonical file name also for the "already open" check
[GPXSee.git] / src / GUI / gui.cpp
blob0423b21f6eb487552071f0edb74d6706d369dcfe
1 #include <QApplication>
2 #include <QSplitter>
3 #include <QVBoxLayout>
4 #include <QMenuBar>
5 #include <QStatusBar>
6 #include <QMessageBox>
7 #include <QCheckBox>
8 #include <QFileDialog>
9 #include <QPrintDialog>
10 #include <QPainter>
11 #include <QPaintEngine>
12 #include <QPaintDevice>
13 #include <QKeyEvent>
14 #include <QMenu>
15 #include <QToolBar>
16 #include <QTabWidget>
17 #include <QActionGroup>
18 #include <QAction>
19 #include <QLabel>
20 #include <QSettings>
21 #include <QLocale>
22 #include <QMimeData>
23 #include <QUrl>
24 #include <QPixmapCache>
25 #include <QWindow>
26 #include <QScreen>
27 #include <QStyle>
28 #include <QTabBar>
29 #include <QGeoPositionInfoSource>
30 #include "common/config.h"
31 #include "common/programpaths.h"
32 #include "common/downloader.h"
33 #include "data/data.h"
34 #include "data/poi.h"
35 #include "data/demloader.h"
36 #include "map/maplist.h"
37 #include "map/emptymap.h"
38 #include "map/crs.h"
39 #include "icons.h"
40 #include "keys.h"
41 #include "settings.h"
42 #include "elevationgraph.h"
43 #include "speedgraph.h"
44 #include "heartrategraph.h"
45 #include "temperaturegraph.h"
46 #include "cadencegraph.h"
47 #include "powergraph.h"
48 #include "gearratiograph.h"
49 #include "mapview.h"
50 #include "trackinfo.h"
51 #include "filebrowser.h"
52 #include "graphtab.h"
53 #include "graphitem.h"
54 #include "pathitem.h"
55 #include "mapaction.h"
56 #include "poiaction.h"
57 #include "gui.h"
58 #ifdef Q_OS_ANDROID
59 #include "common/util.h"
60 #include "navigationwidget.h"
61 #endif // Q_OS_ANDROID
64 #define MAX_RECENT_FILES 10
65 #define TOOLBAR_ICON_SIZE 22
67 GUI::GUI()
69 QString activeMap;
70 QStringList disabledPOIs, recentFiles;
72 _poi = new POI(this);
73 _dem = new DEMLoader(ProgramPaths::demDir(true), this);
74 connect(_dem, &DEMLoader::finished, this, &GUI::demLoaded);
76 createMapView();
77 createGraphTabs();
78 createStatusBar();
79 createActions();
80 createMenus();
81 #ifdef Q_OS_ANDROID
82 createNavigation();
83 #else // Q_OS_ANDROID
84 createToolBars();
85 #endif // Q_OS_ANDROID
86 createBrowser();
88 _splitter = new QSplitter();
89 _splitter->setOrientation(Qt::Vertical);
90 _splitter->setChildrenCollapsible(false);
91 _splitter->addWidget(_mapView);
92 _splitter->addWidget(_graphTabWidget);
93 _splitter->setContentsMargins(0, 0, 0, 0);
94 _splitter->setStretchFactor(0, 255);
95 _splitter->setStretchFactor(1, 1);
97 setCentralWidget(_splitter);
99 setWindowIcon(QIcon(APP_ICON));
100 setWindowTitle(APP_NAME);
101 setUnifiedTitleAndToolBarOnMac(true);
102 setAcceptDrops(true);
104 _trackCount = 0;
105 _routeCount = 0;
106 _waypointCount = 0;
107 _areaCount = 0;
108 _trackDistance = 0;
109 _routeDistance = 0;
110 _time = 0;
111 _movingTime = 0;
112 _lastTab = 0;
114 readSettings(activeMap, disabledPOIs, recentFiles);
116 loadInitialMaps(activeMap);
117 loadInitialPOIs(disabledPOIs);
118 #ifndef Q_OS_ANDROID
119 loadRecentFiles(recentFiles);
120 #endif // Q_OS_ANDROID
122 updateGraphTabs();
123 updateStatusBarInfo();
126 void GUI::createBrowser()
128 _browser = new FileBrowser(this);
129 _browser->setFilter(Data::filter());
130 connect(_browser, &FileBrowser::listChanged, this,
131 &GUI::updateNavigationActions);
134 TreeNode<MapAction*> GUI::createMapActionsNode(const TreeNode<Map*> &node)
136 TreeNode<MapAction*> tree(node.name());
138 for (int i = 0; i < node.childs().size(); i++)
139 tree.addChild(createMapActionsNode(node.childs().at(i)));
141 for (int i = 0; i < node.items().size(); i++) {
142 Map *map = node.items().at(i);
143 if (map->isValid()) {
144 MapAction *a = new MapAction(map, _mapsActionGroup);
145 connect(a, &MapAction::loaded, this, &GUI::mapInitialized);
146 tree.addItem(a);
147 } else
148 delete map;
151 return tree;
154 void GUI::mapInitialized()
156 MapAction *action = static_cast<MapAction*>(QObject::sender());
157 Map *map = action->data().value<Map*>();
159 if (map->isValid()) {
160 if (!_mapsActionGroup->checkedAction())
161 action->trigger();
162 _showMapAction->setEnabled(true);
163 _clearMapCacheAction->setEnabled(true);
164 } else {
165 qWarning("%s: %s", qPrintable(map->path()), qPrintable(map->errorString()));
166 action->deleteLater();
170 TreeNode<POIAction *> GUI::createPOIActionsNode(const TreeNode<QString> &node)
172 TreeNode<POIAction*> tree(node.name());
174 for (int i = 0; i < node.childs().size(); i++)
175 tree.addChild(createPOIActionsNode(node.childs().at(i)));
176 for (int i = 0; i < node.items().size(); i++)
177 tree.addItem(new POIAction(node.items().at(i), _poisActionGroup));
179 return tree;
182 void GUI::createActions()
184 QActionGroup *ag;
186 // Action Groups
187 _fileActionGroup = new QActionGroup(this);
188 _fileActionGroup->setExclusive(false);
189 _fileActionGroup->setEnabled(false);
191 _navigationActionGroup = new QActionGroup(this);
192 _navigationActionGroup->setEnabled(false);
194 // General actions
195 #if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
196 _exitAction = new QAction(QIcon::fromTheme(QUIT_NAME, QIcon(QUIT_ICON)),
197 tr("Quit"), this);
198 _exitAction->setShortcut(QUIT_SHORTCUT);
199 _exitAction->setMenuRole(QAction::QuitRole);
200 connect(_exitAction, &QAction::triggered, this, &GUI::close);
201 addAction(_exitAction);
202 #endif // Q_OS_MAC + Q_OS_ANDROID
204 // Help & About
205 _pathsAction = new QAction(tr("Paths"), this);
206 _pathsAction->setMenuRole(QAction::NoRole);
207 connect(_pathsAction, &QAction::triggered, this, &GUI::paths);
208 #ifndef Q_OS_ANDROID
209 _keysAction = new QAction(tr("Keyboard controls"), this);
210 _keysAction->setMenuRole(QAction::NoRole);
211 connect(_keysAction, &QAction::triggered, this, &GUI::keys);
212 #endif // Q_OS_ANDROID
213 _aboutAction = new QAction(QIcon(APP_ICON), tr("About GPXSee"), this);
214 _aboutAction->setMenuRole(QAction::AboutRole);
215 connect(_aboutAction, &QAction::triggered, this, &GUI::about);
217 // File actions
218 _openFileAction = new QAction(QIcon::fromTheme(OPEN_FILE_NAME,
219 QIcon(OPEN_FILE_ICON)), tr("Open..."), this);
220 _openFileAction->setMenuRole(QAction::NoRole);
221 _openFileAction->setShortcut(OPEN_SHORTCUT);
222 connect(_openFileAction, &QAction::triggered, this,
223 QOverload<>::of(&GUI::openFile));
224 addAction(_openFileAction);
225 _openDirAction = new QAction(QIcon::fromTheme(OPEN_DIR_NAME,
226 QIcon(OPEN_DIR_ICON)), tr("Open directory..."), this);
227 _openDirAction->setMenuRole(QAction::NoRole);
228 connect(_openDirAction, &QAction::triggered, this,
229 QOverload<>::of(&GUI::openDir));
230 _printFileAction = new QAction(QIcon::fromTheme(PRINT_FILE_NAME,
231 QIcon(PRINT_FILE_ICON)), tr("Print..."), this);
232 _printFileAction->setMenuRole(QAction::NoRole);
233 _printFileAction->setActionGroup(_fileActionGroup);
234 connect(_printFileAction, &QAction::triggered, this, &GUI::printFile);
235 addAction(_printFileAction);
236 _exportPDFFileAction = new QAction(QIcon::fromTheme(EXPORT_FILE_NAME,
237 QIcon(EXPORT_FILE_ICON)), tr("Export to PDF..."), this);
238 _exportPDFFileAction->setMenuRole(QAction::NoRole);
239 _exportPDFFileAction->setShortcut(PDF_EXPORT_SHORTCUT);
240 _exportPDFFileAction->setActionGroup(_fileActionGroup);
241 connect(_exportPDFFileAction, &QAction::triggered, this, &GUI::exportPDFFile);
242 addAction(_exportPDFFileAction);
243 _exportPNGFileAction = new QAction(QIcon::fromTheme(EXPORT_FILE_NAME,
244 QIcon(EXPORT_FILE_ICON)), tr("Export to PNG..."), this);
245 _exportPNGFileAction->setMenuRole(QAction::NoRole);
246 _exportPNGFileAction->setShortcut(PNG_EXPORT_SHORTCUT);
247 _exportPNGFileAction->setActionGroup(_fileActionGroup);
248 connect(_exportPNGFileAction, &QAction::triggered, this, &GUI::exportPNGFile);
249 addAction(_exportPNGFileAction);
250 _closeFileAction = new QAction(QIcon::fromTheme(CLOSE_FILE_NAME,
251 QIcon(CLOSE_FILE_ICON)), tr("Close"), this);
252 _closeFileAction->setMenuRole(QAction::NoRole);
253 _closeFileAction->setShortcut(CLOSE_SHORTCUT);
254 _closeFileAction->setActionGroup(_fileActionGroup);
255 connect(_closeFileAction, &QAction::triggered, this, &GUI::closeAll);
256 addAction(_closeFileAction);
257 _reloadFileAction = new QAction(QIcon::fromTheme(RELOAD_FILE_NAME,
258 QIcon(RELOAD_FILE_ICON)), tr("Reload"), this);
259 _reloadFileAction->setMenuRole(QAction::NoRole);
260 _reloadFileAction->setShortcut(RELOAD_SHORTCUT);
261 _reloadFileAction->setActionGroup(_fileActionGroup);
262 connect(_reloadFileAction, &QAction::triggered, this, &GUI::reloadFiles);
263 addAction(_reloadFileAction);
264 _statisticsAction = new QAction(tr("Statistics..."), this);
265 _statisticsAction->setMenuRole(QAction::NoRole);
266 _statisticsAction->setShortcut(STATISTICS_SHORTCUT);
267 _statisticsAction->setActionGroup(_fileActionGroup);
268 connect(_statisticsAction, &QAction::triggered, this, &GUI::statistics);
269 addAction(_statisticsAction);
270 #ifndef Q_OS_ANDROID
271 _recentFilesActionGroup = new QActionGroup(this);
272 connect(_recentFilesActionGroup, &QActionGroup::triggered, this,
273 &GUI::recentFileSelected);
274 _clearRecentFilesAction = new QAction(tr("Clear list"), this);
275 _clearRecentFilesAction->setMenuRole(QAction::NoRole);
276 connect(_clearRecentFilesAction, &QAction::triggered, this,
277 &GUI::clearRecentFiles);
278 #endif // Q_OS_ANDROID
280 // POI actions
281 _poisActionGroup = new QActionGroup(this);
282 _poisActionGroup->setExclusive(false);
283 connect(_poisActionGroup, &QActionGroup::triggered, this,
284 &GUI::poiFileChecked);
285 _openPOIAction = new QAction(QIcon::fromTheme(OPEN_FILE_NAME,
286 QIcon(OPEN_FILE_ICON)), tr("Load POI file..."), this);
287 _openPOIAction->setMenuRole(QAction::NoRole);
288 connect(_openPOIAction, &QAction::triggered, this,
289 QOverload<>::of(&GUI::openPOIFile));
290 _selectAllPOIAction = new QAction(tr("Select all files"), this);
291 _selectAllPOIAction->setMenuRole(QAction::NoRole);
292 _selectAllPOIAction->setEnabled(false);
293 connect(_selectAllPOIAction, &QAction::triggered, this,
294 &GUI::selectAllPOIs);
295 _unselectAllPOIAction = new QAction(tr("Unselect all files"), this);
296 _unselectAllPOIAction->setMenuRole(QAction::NoRole);
297 _unselectAllPOIAction->setEnabled(false);
298 connect(_unselectAllPOIAction, &QAction::triggered, this,
299 &GUI::unselectAllPOIs);
300 _overlapPOIAction = new QAction(tr("Overlap POIs"), this);
301 _overlapPOIAction->setMenuRole(QAction::NoRole);
302 _overlapPOIAction->setCheckable(true);
303 connect(_overlapPOIAction, &QAction::triggered, _mapView,
304 &MapView::showOverlappedPOIs);
305 _showPOIIconsAction = new QAction(tr("Show POI icons"), this);
306 _showPOIIconsAction->setMenuRole(QAction::NoRole);
307 _showPOIIconsAction->setCheckable(true);
308 connect(_showPOIIconsAction, &QAction::triggered, _mapView,
309 &MapView::showPOIIcons);
310 _showPOILabelsAction = new QAction(tr("Show POI labels"), this);
311 _showPOILabelsAction->setMenuRole(QAction::NoRole);
312 _showPOILabelsAction->setCheckable(true);
313 connect(_showPOILabelsAction, &QAction::triggered, _mapView,
314 &MapView::showPOILabels);
315 _showPOIAction = new QAction(QIcon::fromTheme(SHOW_POI_NAME,
316 QIcon(SHOW_POI_ICON)), tr("Show POIs"), this);
317 _showPOIAction->setMenuRole(QAction::NoRole);
318 _showPOIAction->setCheckable(true);
319 _showPOIAction->setShortcut(SHOW_POI_SHORTCUT);
320 connect(_showPOIAction, &QAction::triggered, _mapView, &MapView::showPOI);
321 addAction(_showPOIAction);
323 // Map actions
324 _mapsActionGroup = new QActionGroup(this);
325 _mapsActionGroup->setExclusive(true);
326 connect(_mapsActionGroup, &QActionGroup::triggered, this, &GUI::mapChanged);
327 _showMapAction = new QAction(QIcon::fromTheme(SHOW_MAP_NAME,
328 QIcon(SHOW_MAP_ICON)), tr("Show map"), this);
329 _showMapAction->setEnabled(false);
330 _showMapAction->setMenuRole(QAction::NoRole);
331 _showMapAction->setCheckable(true);
332 _showMapAction->setShortcut(SHOW_MAP_SHORTCUT);
333 connect(_showMapAction, &QAction::triggered, _mapView,
334 &MapView::showMap);
335 addAction(_showMapAction);
336 _loadMapAction = new QAction(QIcon::fromTheme(OPEN_FILE_NAME,
337 QIcon(OPEN_FILE_ICON)), tr("Load map..."), this);
338 _loadMapAction->setMenuRole(QAction::NoRole);
339 connect(_loadMapAction, &QAction::triggered, this,
340 QOverload<>::of(&GUI::loadMap));
341 _loadMapDirAction = new QAction(QIcon::fromTheme(OPEN_DIR_NAME,
342 QIcon(OPEN_DIR_ICON)), tr("Load map directory..."), this);
343 _loadMapDirAction->setMenuRole(QAction::NoRole);
344 connect(_loadMapDirAction, &QAction::triggered, this, &GUI::loadMapDir);
345 _clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
346 _clearMapCacheAction->setEnabled(false);
347 _clearMapCacheAction->setMenuRole(QAction::NoRole);
348 connect(_clearMapCacheAction, &QAction::triggered, this,
349 &GUI::clearMapCache);
350 _nextMapAction = new QAction(tr("Next map"), this);
351 _nextMapAction->setMenuRole(QAction::NoRole);
352 _nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
353 connect(_nextMapAction, &QAction::triggered, this, &GUI::nextMap);
354 addAction(_nextMapAction);
355 _prevMapAction = new QAction(tr("Next map"), this);
356 _prevMapAction->setMenuRole(QAction::NoRole);
357 _prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
358 connect(_prevMapAction, &QAction::triggered, this, &GUI::prevMap);
359 addAction(_prevMapAction);
360 _showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this);
361 _showCoordinatesAction->setMenuRole(QAction::NoRole);
362 _showCoordinatesAction->setCheckable(true);
363 connect(_showCoordinatesAction, &QAction::triggered, _mapView,
364 &MapView::showCursorCoordinates);
366 // Position
367 _showPositionAction = new QAction(QIcon::fromTheme(SHOW_POS_NAME,
368 QIcon(SHOW_POS_ICON)), tr("Show position"), this);
369 _showPositionAction->setMenuRole(QAction::NoRole);
370 _showPositionAction->setCheckable(true);
371 _showPositionAction->setEnabled(false);
372 connect(_showPositionAction, &QAction::triggered, _mapView,
373 &MapView::showPosition);
374 _followPositionAction = new QAction(tr("Follow position"), this);
375 _followPositionAction->setMenuRole(QAction::NoRole);
376 _followPositionAction->setCheckable(true);
377 connect(_followPositionAction, &QAction::triggered, _mapView,
378 &MapView::followPosition);
379 _showPositionCoordinatesAction = new QAction(tr("Show coordinates"),
380 this);
381 _showPositionCoordinatesAction->setMenuRole(QAction::NoRole);
382 _showPositionCoordinatesAction->setCheckable(true);
383 connect(_showPositionCoordinatesAction, &QAction::triggered, _mapView,
384 &MapView::showPositionCoordinates);
385 _showMotionInfoAction = new QAction(tr("Show motion info"), this);
386 _showMotionInfoAction->setMenuRole(QAction::NoRole);
387 _showMotionInfoAction->setCheckable(true);
388 connect(_showMotionInfoAction, &QAction::triggered, _mapView,
389 &MapView::showMotionInfo);
391 // Data actions
392 _showTracksAction = new QAction(tr("Show tracks"), this);
393 _showTracksAction->setMenuRole(QAction::NoRole);
394 _showTracksAction->setCheckable(true);
395 _showTracksAction->setShortcut(SHOW_TRACKS_SHORTCUT);
396 connect(_showTracksAction, &QAction::triggered, this, &GUI::showTracks);
397 _showRoutesAction = new QAction(tr("Show routes"), this);
398 _showRoutesAction->setMenuRole(QAction::NoRole);
399 _showRoutesAction->setCheckable(true);
400 connect(_showRoutesAction, &QAction::triggered, this, &GUI::showRoutes);
401 _showWaypointsAction = new QAction(tr("Show waypoints"), this);
402 _showWaypointsAction->setMenuRole(QAction::NoRole);
403 _showWaypointsAction->setCheckable(true);
404 connect(_showWaypointsAction, &QAction::triggered, this,
405 &GUI::showWaypoints);
406 _showAreasAction = new QAction(tr("Show areas"), this);
407 _showAreasAction->setMenuRole(QAction::NoRole);
408 _showAreasAction->setCheckable(true);
409 connect(_showAreasAction, &QAction::triggered, this, &GUI::showAreas);
410 _showWaypointIconsAction = new QAction(tr("Waypoint icons"), this);
411 _showWaypointIconsAction->setMenuRole(QAction::NoRole);
412 _showWaypointIconsAction->setCheckable(true);
413 connect(_showWaypointIconsAction, &QAction::triggered, _mapView,
414 &MapView::showWaypointIcons);
415 _showWaypointLabelsAction = new QAction(tr("Waypoint labels"), this);
416 _showWaypointLabelsAction->setMenuRole(QAction::NoRole);
417 _showWaypointLabelsAction->setCheckable(true);
418 connect(_showWaypointLabelsAction, &QAction::triggered, _mapView,
419 &MapView::showWaypointLabels);
420 _showRouteWaypointsAction = new QAction(tr("Route waypoints"), this);
421 _showRouteWaypointsAction->setMenuRole(QAction::NoRole);
422 _showRouteWaypointsAction->setCheckable(true);
423 connect(_showRouteWaypointsAction, &QAction::triggered, _mapView,
424 &MapView::showRouteWaypoints);
425 _showTicksAction = new QAction(tr("km/mi markers"), this);
426 _showTicksAction->setMenuRole(QAction::NoRole);
427 _showTicksAction->setCheckable(true);
428 connect(_showTicksAction, &QAction::triggered, _mapView,
429 &MapView::showTicks);
430 QActionGroup *markerInfoGroup = new QActionGroup(this);
431 connect(markerInfoGroup, &QActionGroup::triggered, this,
432 &GUI::showPathMarkerInfo);
433 _hideMarkersAction = new QAction(tr("Do not show"), this);
434 _hideMarkersAction->setMenuRole(QAction::NoRole);
435 _hideMarkersAction->setCheckable(true);
436 _hideMarkersAction->setActionGroup(markerInfoGroup);
437 _showMarkersAction = new QAction(tr("Marker only"), this);
438 _showMarkersAction->setMenuRole(QAction::NoRole);
439 _showMarkersAction->setCheckable(true);
440 _showMarkersAction->setActionGroup(markerInfoGroup);
441 _showMarkerDateAction = new QAction(tr("Date/time"), this);
442 _showMarkerDateAction->setMenuRole(QAction::NoRole);
443 _showMarkerDateAction->setCheckable(true);
444 _showMarkerDateAction->setActionGroup(markerInfoGroup);
445 _showMarkerCoordinatesAction = new QAction(tr("Coordinates"), this);
446 _showMarkerCoordinatesAction->setMenuRole(QAction::NoRole);
447 _showMarkerCoordinatesAction->setCheckable(true);
448 _showMarkerCoordinatesAction->setActionGroup(markerInfoGroup);
449 _useStylesAction = new QAction(tr("Use styles"), this);
450 _useStylesAction->setMenuRole(QAction::NoRole);
451 _useStylesAction->setCheckable(true);
452 connect(_useStylesAction, &QAction::triggered, _mapView,
453 &MapView::useStyles);
455 // DEM actions
456 _downloadDEMAction = new QAction(tr("Download DEM data"), this);
457 _downloadDEMAction->setMenuRole(QAction::NoRole);
458 _downloadDEMAction->setEnabled(false);
459 _downloadDEMAction->setShortcut(DOWNLOAD_DEM_SHORTCUT);
460 connect(_downloadDEMAction, &QAction::triggered, this, &GUI::downloadDEM);
461 _showDEMTilesAction = new QAction(tr("Show local DEM tiles"), this);
462 _showDEMTilesAction->setMenuRole(QAction::NoRole);
463 connect(_showDEMTilesAction, &QAction::triggered, this, &GUI::showDEMTiles);
465 // Graph actions
466 _showGraphsAction = new QAction(QIcon::fromTheme(SHOW_GRAPHS_NAME,
467 QIcon(SHOW_GRAPHS_ICON)), tr("Show graphs"), this);
468 _showGraphsAction->setMenuRole(QAction::NoRole);
469 _showGraphsAction->setCheckable(true);
470 _showGraphsAction->setShortcut(SHOW_GRAPHS_SHORTCUT);
471 connect(_showGraphsAction, &QAction::triggered, this, &GUI::showGraphs);
472 addAction(_showGraphsAction);
473 ag = new QActionGroup(this);
474 ag->setExclusive(true);
475 _distanceGraphAction = new QAction(tr("Distance"), this);
476 _distanceGraphAction->setMenuRole(QAction::NoRole);
477 _distanceGraphAction->setCheckable(true);
478 _distanceGraphAction->setActionGroup(ag);
479 connect(_distanceGraphAction, &QAction::triggered, this,
480 &GUI::setDistanceGraph);
481 addAction(_distanceGraphAction);
482 _timeGraphAction = new QAction(tr("Time"), this);
483 _timeGraphAction->setMenuRole(QAction::NoRole);
484 _timeGraphAction->setCheckable(true);
485 _timeGraphAction->setActionGroup(ag);
486 connect(_timeGraphAction, &QAction::triggered, this, &GUI::setTimeGraph);
487 addAction(_timeGraphAction);
488 _showGraphGridAction = new QAction(tr("Show grid"), this);
489 _showGraphGridAction->setMenuRole(QAction::NoRole);
490 _showGraphGridAction->setCheckable(true);
491 connect(_showGraphGridAction, &QAction::triggered, this,
492 &GUI::showGraphGrids);
493 _showGraphSliderInfoAction = new QAction(tr("Show slider info"), this);
494 _showGraphSliderInfoAction->setMenuRole(QAction::NoRole);
495 _showGraphSliderInfoAction->setCheckable(true);
496 connect(_showGraphSliderInfoAction, &QAction::triggered, this,
497 &GUI::showGraphSliderInfo);
498 #ifdef Q_OS_ANDROID
499 _showGraphTabsAction = new QAction(tr("Show tabs"), this);
500 _showGraphTabsAction->setMenuRole(QAction::NoRole);
501 _showGraphTabsAction->setCheckable(true);
502 connect(_showGraphTabsAction, &QAction::triggered, this,
503 &GUI::showGraphTabs);
504 #endif // Q_OS_ANDROID
506 // Settings actions
507 #ifndef Q_OS_ANDROID
508 _showToolbarsAction = new QAction(tr("Show toolbars"), this);
509 _showToolbarsAction->setMenuRole(QAction::NoRole);
510 _showToolbarsAction->setCheckable(true);
511 connect(_showToolbarsAction, &QAction::triggered, this, &GUI::showToolbars);
512 #endif // Q_OS_ANDROID
513 ag = new QActionGroup(this);
514 ag->setExclusive(true);
515 _totalTimeAction = new QAction(tr("Total time"), this);
516 _totalTimeAction->setMenuRole(QAction::NoRole);
517 _totalTimeAction->setCheckable(true);
518 _totalTimeAction->setActionGroup(ag);
519 connect(_totalTimeAction, &QAction::triggered, this, &GUI::setTotalTime);
520 _movingTimeAction = new QAction(tr("Moving time"), this);
521 _movingTimeAction->setMenuRole(QAction::NoRole);
522 _movingTimeAction->setCheckable(true);
523 _movingTimeAction->setActionGroup(ag);
524 connect(_movingTimeAction, &QAction::triggered, this, &GUI::setMovingTime);
525 ag = new QActionGroup(this);
526 ag->setExclusive(true);
527 _metricUnitsAction = new QAction(tr("Metric"), this);
528 _metricUnitsAction->setMenuRole(QAction::NoRole);
529 _metricUnitsAction->setCheckable(true);
530 _metricUnitsAction->setActionGroup(ag);
531 connect(_metricUnitsAction, &QAction::triggered, this, &GUI::setMetricUnits);
532 _imperialUnitsAction = new QAction(tr("Imperial"), this);
533 _imperialUnitsAction->setMenuRole(QAction::NoRole);
534 _imperialUnitsAction->setCheckable(true);
535 _imperialUnitsAction->setActionGroup(ag);
536 connect(_imperialUnitsAction, &QAction::triggered, this,
537 &GUI::setImperialUnits);
538 _nauticalUnitsAction = new QAction(tr("Nautical"), this);
539 _nauticalUnitsAction->setMenuRole(QAction::NoRole);
540 _nauticalUnitsAction->setCheckable(true);
541 _nauticalUnitsAction->setActionGroup(ag);
542 connect(_nauticalUnitsAction, &QAction::triggered, this,
543 &GUI::setNauticalUnits);
544 ag = new QActionGroup(this);
545 ag->setExclusive(true);
546 _decimalDegreesAction = new QAction(tr("Decimal degrees (DD)"), this);
547 _decimalDegreesAction->setMenuRole(QAction::NoRole);
548 _decimalDegreesAction->setCheckable(true);
549 _decimalDegreesAction->setActionGroup(ag);
550 connect(_decimalDegreesAction, &QAction::triggered, this,
551 &GUI::setDecimalDegrees);
552 _degreesMinutesAction = new QAction(tr("Degrees and decimal minutes (DMM)"),
553 this);
554 _degreesMinutesAction->setMenuRole(QAction::NoRole);
555 _degreesMinutesAction->setCheckable(true);
556 _degreesMinutesAction->setActionGroup(ag);
557 connect(_degreesMinutesAction, &QAction::triggered, this,
558 &GUI::setDegreesMinutes);
559 _dmsAction = new QAction(tr("Degrees, minutes, seconds (DMS)"), this);
560 _dmsAction->setMenuRole(QAction::NoRole);
561 _dmsAction->setCheckable(true);
562 _dmsAction->setActionGroup(ag);
563 connect(_dmsAction, &QAction::triggered, this, &GUI::setDMS);
564 #ifndef Q_OS_ANDROID
565 _fullscreenAction = new QAction(QIcon::fromTheme(FULLSCREEN_NAME,
566 QIcon(FULLSCREEN_ICON)), tr("Fullscreen mode"), this);
567 _fullscreenAction->setMenuRole(QAction::NoRole);
568 _fullscreenAction->setCheckable(true);
569 _fullscreenAction->setShortcut(FULLSCREEN_SHORTCUT);
570 connect(_fullscreenAction, &QAction::triggered, this, &GUI::showFullscreen);
571 addAction(_fullscreenAction);
572 #endif // Q_OS_ANDROID
573 _openOptionsAction = new QAction(tr("Options..."), this);
574 _openOptionsAction->setMenuRole(QAction::PreferencesRole);
575 connect(_openOptionsAction, &QAction::triggered, this, &GUI::openOptions);
577 // Navigation actions
578 #ifndef Q_OS_ANDROID
579 _nextAction = new QAction(QIcon::fromTheme(NEXT_FILE_NAME,
580 QIcon(NEXT_FILE_ICON)), tr("Next"), this);
581 _nextAction->setActionGroup(_navigationActionGroup);
582 _nextAction->setMenuRole(QAction::NoRole);
583 connect(_nextAction, &QAction::triggered, this, &GUI::next);
584 _prevAction = new QAction(QIcon::fromTheme(PREV_FILE_NAME,
585 QIcon(PREV_FILE_ICON)), tr("Previous"), this);
586 _prevAction->setMenuRole(QAction::NoRole);
587 _prevAction->setActionGroup(_navigationActionGroup);
588 connect(_prevAction, &QAction::triggered, this, &GUI::prev);
589 _lastAction = new QAction(QIcon::fromTheme(LAST_FILE_NAME,
590 QIcon(LAST_FILE_ICON)), tr("Last"), this);
591 _lastAction->setMenuRole(QAction::NoRole);
592 _lastAction->setActionGroup(_navigationActionGroup);
593 connect(_lastAction, &QAction::triggered, this, &GUI::last);
594 _firstAction = new QAction(QIcon::fromTheme(FIRST_FILE_NAME,
595 QIcon(FIRST_FILE_ICON)), tr("First"), this);
596 _firstAction->setMenuRole(QAction::NoRole);
597 _firstAction->setActionGroup(_navigationActionGroup);
598 connect(_firstAction, &QAction::triggered, this, &GUI::first);
599 #endif // Q_OS_ANDROID
602 void GUI::createMapNodeMenu(const TreeNode<MapAction*> &node, QMenu *menu,
603 QAction *action)
605 for (int i = 0; i < node.childs().size(); i++) {
606 QMenu *cm = new QMenu(node.childs().at(i).name(), menu);
607 menu->insertMenu(action, cm);
608 createMapNodeMenu(node.childs().at(i), cm);
611 for (int i = 0; i < node.items().size(); i++)
612 menu->insertAction(action, node.items().at(i));
615 void GUI::createPOINodeMenu(const TreeNode<POIAction*> &node, QMenu *menu,
616 QAction *action)
618 for (int i = 0; i < node.childs().size(); i++) {
619 QMenu *cm = new QMenu(node.childs().at(i).name(), menu);
620 menu->insertMenu(action, cm);
621 createPOINodeMenu(node.childs().at(i), cm);
624 for (int i = 0; i < node.items().size(); i++)
625 menu->insertAction(action, node.items().at(i));
628 void GUI::createMenus()
630 QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
631 fileMenu->addAction(_openFileAction);
632 #ifndef Q_OS_ANDROID
633 _recentFilesMenu = fileMenu->addMenu(tr("Open recent"));
634 _recentFilesMenu->setIcon(QIcon::fromTheme(OPEN_RECENT_NAME,
635 QIcon(OPEN_RECENT_ICON)));
636 _recentFilesMenu->menuAction()->setMenuRole(QAction::NoRole);
637 _recentFilesMenu->setEnabled(false);
638 _recentFilesEnd = _recentFilesMenu->addSeparator();
639 _recentFilesMenu->addAction(_clearRecentFilesAction);
640 #endif // Q_OS_ANDROID
641 fileMenu->addAction(_openDirAction);
642 fileMenu->addSeparator();
643 #ifndef Q_OS_ANDROID
644 fileMenu->addAction(_printFileAction);
645 #endif // Q_OS_ANDROID
646 fileMenu->addAction(_exportPDFFileAction);
647 fileMenu->addAction(_exportPNGFileAction);
648 fileMenu->addSeparator();
649 fileMenu->addAction(_statisticsAction);
650 fileMenu->addSeparator();
651 fileMenu->addAction(_reloadFileAction);
652 fileMenu->addAction(_closeFileAction);
653 #if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
654 fileMenu->addSeparator();
655 fileMenu->addAction(_exitAction);
656 #endif // Q_OS_MAC + Q_OS_ANDROID
658 _mapMenu = menuBar()->addMenu(tr("&Map"));
659 _mapsEnd = _mapMenu->addSeparator();
660 _mapMenu->addAction(_loadMapAction);
661 _mapMenu->addAction(_loadMapDirAction);
662 _mapMenu->addAction(_clearMapCacheAction);
663 _mapMenu->addSeparator();
664 _mapMenu->addAction(_showCoordinatesAction);
665 _mapMenu->addSeparator();
666 _mapMenu->addAction(_showMapAction);
668 QMenu *graphMenu = menuBar()->addMenu(tr("&Graph"));
669 graphMenu->addAction(_distanceGraphAction);
670 graphMenu->addAction(_timeGraphAction);
671 graphMenu->addSeparator();
672 graphMenu->addAction(_showGraphGridAction);
673 graphMenu->addAction(_showGraphSliderInfoAction);
674 #ifdef Q_OS_ANDROID
675 graphMenu->addAction(_showGraphTabsAction);
676 #endif // Q_OS_ANDROID
677 graphMenu->addSeparator();
678 graphMenu->addAction(_showGraphsAction);
680 QMenu *dataMenu = menuBar()->addMenu(tr("&Data"));
681 dataMenu->addAction(_showWaypointIconsAction);
682 dataMenu->addAction(_showWaypointLabelsAction);
683 dataMenu->addAction(_showRouteWaypointsAction);
684 dataMenu->addAction(_showTicksAction);
685 QMenu *markerMenu = dataMenu->addMenu(tr("Position info"));
686 markerMenu->menuAction()->setMenuRole(QAction::NoRole);
687 markerMenu->addAction(_hideMarkersAction);
688 markerMenu->addAction(_showMarkersAction);
689 markerMenu->addAction(_showMarkerDateAction);
690 markerMenu->addAction(_showMarkerCoordinatesAction);
691 dataMenu->addSeparator();
692 dataMenu->addAction(_useStylesAction);
693 dataMenu->addSeparator();
694 dataMenu->addAction(_showTracksAction);
695 dataMenu->addAction(_showRoutesAction);
696 dataMenu->addAction(_showAreasAction);
697 dataMenu->addAction(_showWaypointsAction);
699 _poiMenu = menuBar()->addMenu(tr("&POI"));
700 _poisEnd = _poiMenu->addSeparator();
701 _poiMenu->addAction(_openPOIAction);
702 _poiMenu->addAction(_selectAllPOIAction);
703 _poiMenu->addAction(_unselectAllPOIAction);
704 _poiMenu->addSeparator();
705 _poiMenu->addAction(_showPOIIconsAction);
706 _poiMenu->addAction(_showPOILabelsAction);
707 _poiMenu->addAction(_overlapPOIAction);
708 _poiMenu->addSeparator();
709 _poiMenu->addAction(_showPOIAction);
711 QMenu *demMenu = menuBar()->addMenu(tr("DEM"));
712 demMenu->addAction(_showDEMTilesAction);
713 demMenu->addAction(_downloadDEMAction);
715 QMenu *positionMenu = menuBar()->addMenu(tr("Position"));
716 positionMenu->addAction(_showPositionCoordinatesAction);
717 positionMenu->addAction(_showMotionInfoAction);
718 positionMenu->addAction(_followPositionAction);
719 positionMenu->addSeparator();
720 positionMenu->addAction(_showPositionAction);
722 QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
723 QMenu *timeMenu = settingsMenu->addMenu(tr("Time"));
724 timeMenu->menuAction()->setMenuRole(QAction::NoRole);
725 timeMenu->addAction(_totalTimeAction);
726 timeMenu->addAction(_movingTimeAction);
727 QMenu *unitsMenu = settingsMenu->addMenu(tr("Units"));
728 unitsMenu->menuAction()->setMenuRole(QAction::NoRole);
729 unitsMenu->addAction(_metricUnitsAction);
730 unitsMenu->addAction(_imperialUnitsAction);
731 unitsMenu->addAction(_nauticalUnitsAction);
732 QMenu *coordinatesMenu = settingsMenu->addMenu(tr("Coordinates format"));
733 coordinatesMenu->menuAction()->setMenuRole(QAction::NoRole);
734 coordinatesMenu->addAction(_decimalDegreesAction);
735 coordinatesMenu->addAction(_degreesMinutesAction);
736 coordinatesMenu->addAction(_dmsAction);
737 settingsMenu->addSeparator();
738 #ifndef Q_OS_ANDROID
739 settingsMenu->addAction(_showToolbarsAction);
740 settingsMenu->addAction(_fullscreenAction);
741 settingsMenu->addSeparator();
742 #endif // Q_OS_ANDROID
743 settingsMenu->addAction(_openOptionsAction);
745 QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
746 helpMenu->addAction(_pathsAction);
747 #ifndef Q_OS_ANDROID
748 helpMenu->addAction(_keysAction);
749 #endif // Q_OS_ANDROID
750 helpMenu->addSeparator();
751 helpMenu->addAction(_aboutAction);
754 #ifdef Q_OS_ANDROID
755 void GUI::createNavigation()
757 _navigation = new NavigationWidget(_mapView);
759 connect(_navigation, &NavigationWidget::next, this, &GUI::next);
760 connect(_navigation, &NavigationWidget::prev, this, &GUI::prev);
762 #else // Q_OS_ANDROID
763 void GUI::createToolBars()
765 int is = style()->pixelMetric(QStyle::PM_ToolBarIconSize);
766 QSize iconSize(qMin(is, TOOLBAR_ICON_SIZE), qMin(is, TOOLBAR_ICON_SIZE));
768 #ifdef Q_OS_MAC
769 setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
770 #endif // Q_OS_MAC
772 _fileToolBar = addToolBar(tr("File"));
773 _fileToolBar->setObjectName("File");
774 _fileToolBar->setIconSize(iconSize);
775 _fileToolBar->addAction(_openFileAction);
776 _fileToolBar->addAction(_reloadFileAction);
777 _fileToolBar->addAction(_closeFileAction);
778 #ifndef Q_OS_MAC
779 _fileToolBar->addAction(_printFileAction);
780 #endif // Q_OS_MAC
782 _showToolBar = addToolBar(tr("Show"));
783 _showToolBar->setObjectName("Show");
784 _showToolBar->setIconSize(iconSize);
785 _showToolBar->addAction(_showPOIAction);
786 _showToolBar->addAction(_showMapAction);
787 _showToolBar->addAction(_showGraphsAction);
788 _showToolBar->addAction(_showPositionAction);
790 _navigationToolBar = addToolBar(tr("Navigation"));
791 _navigationToolBar->setObjectName("Navigation");
792 _navigationToolBar->setIconSize(iconSize);
793 _navigationToolBar->addAction(_firstAction);
794 _navigationToolBar->addAction(_prevAction);
795 _navigationToolBar->addAction(_nextAction);
796 _navigationToolBar->addAction(_lastAction);
798 #endif // Q_OS_ANDROID
800 void GUI::createMapView()
802 _map = new EmptyMap(this);
803 _mapView = new MapView(_map, _poi, this);
804 _mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
805 QSizePolicy::Expanding));
806 #ifdef Q_OS_ANDROID
807 _mapView->setMinimumHeight(100);
808 #else // Q_OS_ANDROID
809 _mapView->setMinimumHeight(200);
810 #endif // Q_OS_ANDROID
811 #ifdef Q_OS_WIN32
812 _mapView->setFrameShape(QFrame::NoFrame);
813 #endif // Q_OS_WIN32
816 void GUI::createGraphTabs()
818 _graphTabWidget = new QTabWidget();
819 _graphTabWidget->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
820 QSizePolicy::Preferred));
821 _graphTabWidget->setMinimumHeight(200);
822 #ifndef Q_OS_MAC
823 _graphTabWidget->setDocumentMode(true);
824 #endif // Q_OS_MAC
826 connect(_graphTabWidget, &QTabWidget::currentChanged, this,
827 &GUI::graphChanged);
829 _tabs.append(new ElevationGraph(_graphTabWidget));
830 _tabs.append(new SpeedGraph(_graphTabWidget));
831 _tabs.append(new HeartRateGraph(_graphTabWidget));
832 _tabs.append(new CadenceGraph(_graphTabWidget));
833 _tabs.append(new PowerGraph(_graphTabWidget));
834 _tabs.append(new TemperatureGraph(_graphTabWidget));
835 _tabs.append(new GearRatioGraph(_graphTabWidget));
837 for (int i = 0; i < _tabs.size(); i++)
838 connect(_tabs.at(i), &GraphTab::sliderPositionChanged, _mapView,
839 &MapView::setMarkerPosition);
842 void GUI::createStatusBar()
844 _fileNameLabel = new QLabel();
845 _distanceLabel = new QLabel();
846 _timeLabel = new QLabel();
847 _distanceLabel->setAlignment(Qt::AlignHCenter);
848 _timeLabel->setAlignment(Qt::AlignHCenter);
850 statusBar()->addPermanentWidget(_fileNameLabel, 8);
851 statusBar()->addPermanentWidget(_distanceLabel, 1);
852 statusBar()->addPermanentWidget(_timeLabel, 1);
853 statusBar()->setSizeGripEnabled(false);
856 void GUI::about()
858 QMessageBox msgBox(this);
859 QUrl homepage(APP_HOMEPAGE);
861 msgBox.setWindowTitle(tr("About GPXSee"));
862 #ifdef Q_OS_ANDROID
863 msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p>" + tr("Version %1")
864 .arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture()
865 + ", Qt " + QT_VERSION_STR + ")") + "</p><p>"
866 + tr("GPXSee is distributed under the terms of the GNU General Public "
867 "License version 3. For more info about GPXSee visit the project "
868 "homepage at %1.").arg("<a href=\"" + homepage.toString() + "\">"
869 + homepage.toString(QUrl::RemoveScheme).mid(2) + "</a>") + "</p>");
870 #else // Q_OS_ANDROID
871 msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p>" + tr("Version %1")
872 .arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture()
873 + ", Qt " + QT_VERSION_STR + ")") + "</p>");
874 msgBox.setInformativeText("<table width=\"300\"><tr><td>"
875 + tr("GPXSee is distributed under the terms of the GNU General Public "
876 "License version 3. For more info about GPXSee visit the project "
877 "homepage at %1.").arg("<a href=\"" + homepage.toString() + "\">"
878 + homepage.toString(QUrl::RemoveScheme).mid(2) + "</a>")
879 + "</td></tr></table>");
881 QIcon icon = msgBox.windowIcon();
882 QSize size = icon.actualSize(QSize(64, 64));
883 msgBox.setIconPixmap(icon.pixmap(size));
884 #endif // Q_OS_ANDROID
886 msgBox.exec();
889 #ifndef Q_OS_ANDROID
890 void GUI::keys()
892 QMessageBox msgBox(this);
894 msgBox.setWindowTitle(tr("Keyboard controls"));
895 msgBox.setText("<h3>" + tr("Keyboard controls") + "</h3>");
896 msgBox.setInformativeText(
897 "<style>td {padding-right: 1.5em;}</style><div><table><tr><td>"
898 + tr("Next file") + "</td><td><i>" + QKeySequence(NEXT_KEY).toString()
899 + "</i></td></tr><tr><td>" + tr("Previous file")
900 + "</td><td><i>" + QKeySequence(PREV_KEY).toString()
901 + "</i></td></tr><tr><td>" + tr("First file") + "</td><td><i>"
902 + QKeySequence(FIRST_KEY).toString() + "</i></td></tr><tr><td>"
903 + tr("Last file") + "</td><td><i>" + QKeySequence(LAST_KEY).toString()
904 + "</i></td></tr><tr><td>" + tr("Append file")
905 + "</td><td><i>" + QKeySequence(MODIFIER).toString() + tr("Next/Previous")
906 + "</i></td></tr><tr><td></td><td></td></tr><tr><td>"
907 + tr("Toggle graph type") + "</td><td><i>"
908 + QKeySequence(TOGGLE_GRAPH_TYPE_KEY).toString() + "</i></td></tr><tr><td>"
909 + tr("Toggle time type") + "</td><td><i>"
910 + QKeySequence(TOGGLE_TIME_TYPE_KEY).toString() + "</i></td></tr><tr><td>"
911 + tr("Toggle position info") + "</td><td><i>"
912 + QKeySequence(TOGGLE_MARKER_INFO_KEY).toString() + "</i></td></tr>"
913 + "<tr><td></td><td></td></tr><tr><td>" + tr("Next map")
914 + "</td><td><i>" + NEXT_MAP_SHORTCUT.toString() + "</i></td></tr><tr><td>"
915 + tr("Previous map") + "</td><td><i>" + PREV_MAP_SHORTCUT.toString()
916 + "</i></td></tr><tr><td></td><td></td></tr><tr><td>" + tr("Zoom in")
917 + "</td><td><i>" + QKeySequence(ZOOM_IN).toString()
918 + "</i></td></tr><tr><td>" + tr("Zoom out") + "</td><td><i>"
919 + QKeySequence(ZOOM_OUT).toString() + "</i></td></tr><tr><td>"
920 + tr("Digital zoom") + "</td><td><i>" + QKeySequence(MODIFIER).toString()
921 + tr("Zoom") + "</i></td></tr><tr><td></td><td></td></tr><tr><td>"
922 + tr("Copy coordinates") + "</td><td><i>"
923 + QKeySequence(MODIFIER).toString() + tr("Left Click")
924 + "</i></td></tr></table></div>");
926 msgBox.exec();
928 #endif // Q_OS_ANDROID
930 void GUI::paths()
932 QMessageBox msgBox(this);
934 msgBox.setWindowTitle(tr("Paths"));
935 #ifdef Q_OS_ANDROID
936 msgBox.setText(
937 + "<small><b>" + tr("Map directory:") + "</b><br>"
938 + QDir::cleanPath(ProgramPaths::mapDir(true)) + "<br><br><b>"
939 + tr("POI directory:") + "</b><br>"
940 + QDir::cleanPath(ProgramPaths::poiDir(true)) + "<br><br><b>"
941 + tr("CRS directory:") + "</b><br>"
942 + QDir::cleanPath(ProgramPaths::crsDir(true)) + "<br><br><b>"
943 + tr("DEM directory:") + "</b><br>"
944 + QDir::cleanPath(ProgramPaths::demDir(true)) + "<br><br><b>"
945 + tr("Styles directory:") + "</b><br>"
946 + QDir::cleanPath(ProgramPaths::styleDir(true)) + "<br><br><b>"
947 + tr("Symbols directory:") + "</b><br>"
948 + QDir::cleanPath(ProgramPaths::symbolsDir(true)) + "<br><br><b>"
949 + tr("Tile cache directory:") + "</b><br>"
950 + QDir::cleanPath(ProgramPaths::tilesDir()) + "</small>");
951 #else // Q_OS_ANDROID
952 msgBox.setText("<h3>" + tr("Paths") + "</h3>");
953 msgBox.setInformativeText(
954 "<style>td {white-space: pre; padding-right: 1em;}</style><table><tr><td>"
955 + tr("Map directory:") + "</td><td><code>"
956 + QDir::cleanPath(ProgramPaths::mapDir(true)) + "</code></td></tr><tr><td>"
957 + tr("POI directory:") + "</td><td><code>"
958 + QDir::cleanPath(ProgramPaths::poiDir(true)) + "</code></td></tr><tr><td>"
959 + tr("CRS directory:") + "</td><td><code>"
960 + QDir::cleanPath(ProgramPaths::crsDir(true)) + "</code></td></tr><tr><td>"
961 + tr("DEM directory:") + "</td><td><code>"
962 + QDir::cleanPath(ProgramPaths::demDir(true)) + "</code></td></tr><tr><td>"
963 + tr("Styles directory:") + "</td><td><code>"
964 + QDir::cleanPath(ProgramPaths::styleDir(true)) + "</code></td></tr><tr><td>"
965 + tr("Symbols directory:") + "</td><td><code>"
966 + QDir::cleanPath(ProgramPaths::symbolsDir(true)) + "</code></td></tr><tr><td>"
967 + tr("Tile cache directory:") + "</td><td><code>"
968 + QDir::cleanPath(ProgramPaths::tilesDir()) + "</code></td></tr></table>"
970 #endif // Q_OS_ANDROID
972 msgBox.exec();
975 void GUI::openFile()
977 #ifdef Q_OS_ANDROID
978 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"),
979 _dataDir));
980 #else // Q_OS_ANDROID
981 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"),
982 _dataDir, Data::formats()));
983 #endif // Q_OS_ANDROID
984 int showError = (files.size() > 1) ? 2 : 1;
986 for (int i = 0; i < files.size(); i++)
987 openFile(files.at(i), true, showError);
988 if (!files.isEmpty())
989 _dataDir = QFileInfo(files.last()).path();
992 #ifndef Q_OS_ANDROID
993 void GUI::openDir(const QString &path, int &showError)
995 QDir md(path);
996 md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
997 md.setSorting(QDir::DirsLast);
998 QFileInfoList ml = md.entryInfoList();
1000 for (int i = 0; i < ml.size(); i++) {
1001 const QFileInfo &fi = ml.at(i);
1003 if (fi.isDir())
1004 openDir(fi.absoluteFilePath(), showError);
1005 else
1006 openFile(fi.absoluteFilePath(), true, showError);
1009 #endif // Q_OS_ANDROID
1011 void GUI::openDir()
1013 QString dir(QFileDialog::getExistingDirectory(this, tr("Open directory"),
1014 _dataDir));
1016 if (!dir.isEmpty()) {
1017 #ifdef Q_OS_ANDROID
1018 int showError = 1;
1019 _browser->setCurrentDir(dir);
1020 openFile(_browser->current(), true, showError);
1021 #else // Q_OS_ANDROID
1022 int showError = 2;
1023 openDir(dir, showError);
1024 _dataDir = dir;
1025 #endif // Q_OS_ANDROID
1029 bool GUI::openFile(const QString &fileName, bool tryUnknown, int &showError)
1031 QFileInfo fi(fileName);
1032 QString canonicalFileName(fi.canonicalFilePath());
1034 if (_files.contains(canonicalFileName))
1035 return true;
1037 if (!loadFile(fileName, tryUnknown, showError))
1038 return false;
1040 _files.append(canonicalFileName);
1041 #ifndef Q_OS_ANDROID
1042 _browser->setCurrent(fileName);
1043 #endif // Q_OS_ANDROID
1044 _fileActionGroup->setEnabled(true);
1045 // Explicitly enable the reload action as it may be disabled by loadMapDir()
1046 _reloadFileAction->setEnabled(true);
1047 _navigationActionGroup->setEnabled(true);
1049 updateNavigationActions();
1050 updateStatusBarInfo();
1051 updateWindowTitle();
1052 #ifndef Q_OS_ANDROID
1053 updateRecentFiles(canonicalFileName);
1054 #endif // Q_OS_ANDROID
1056 return true;
1059 bool GUI::loadFile(const QString &fileName, bool tryUnknown, int &showError)
1061 Data data(fileName, tryUnknown);
1063 if (data.isValid()) {
1064 loadData(data);
1065 return true;
1066 } else {
1067 updateNavigationActions();
1068 updateStatusBarInfo();
1069 updateWindowTitle();
1070 updateGraphTabs();
1071 updateDEMDownloadAction();
1072 if (_files.isEmpty())
1073 _fileActionGroup->setEnabled(false);
1075 if (showError) {
1076 QString error = tr("Error loading data file:") + "\n"
1077 + Util::displayName(fileName) + ": " + data.errorString();
1078 if (data.errorLine())
1079 error.append("\n" + tr("Line: %1").arg(data.errorLine()));
1081 if (showError > 1) {
1082 QMessageBox message(QMessageBox::Critical, APP_NAME, error,
1083 QMessageBox::Ok, this);
1084 QCheckBox checkBox(tr("Don't show again"));
1085 message.setCheckBox(&checkBox);
1086 message.exec();
1087 if (checkBox.isChecked())
1088 showError = 0;
1089 } else
1090 QMessageBox::critical(this, APP_NAME, error);
1093 return false;
1097 void GUI::loadData(const Data &data)
1099 QList<QList<GraphItem*> > graphs;
1100 QList<PathItem*> paths;
1102 for (int i = 0; i < data.tracks().count(); i++) {
1103 const Track &track = data.tracks().at(i);
1104 _trackDistance += track.distance();
1105 _time += track.time();
1106 _movingTime += track.movingTime();
1107 const QDateTime date = track.date().toTimeZone(_options.timeZone.zone());
1108 if (_dateRange.first.isNull() || _dateRange.first > date)
1109 _dateRange.first = date;
1110 if (_dateRange.second.isNull() || _dateRange.second < date)
1111 _dateRange.second = date;
1113 _trackCount += data.tracks().count();
1115 for (int i = 0; i < data.routes().count(); i++)
1116 _routeDistance += data.routes().at(i).distance();
1117 _routeCount += data.routes().count();
1119 _waypointCount += data.waypoints().count();
1120 _areaCount += data.areas().count();
1122 if (_pathName.isNull()) {
1123 if (data.tracks().count() == 1 && !data.routes().count())
1124 _pathName = data.tracks().first().name();
1125 else if (data.routes().count() == 1 && !data.tracks().count())
1126 _pathName = data.routes().first().name();
1127 } else
1128 _pathName = QString();
1130 for (int i = 0; i < _tabs.count(); i++)
1131 graphs.append(_tabs.at(i)->loadData(data));
1132 if (updateGraphTabs())
1133 _splitter->refresh();
1134 paths = _mapView->loadData(data);
1136 GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->currentWidget());
1138 for (int i = 0; i < paths.count(); i++) {
1139 PathItem *pi = paths.at(i);
1140 if (!pi)
1141 continue;
1143 for (int j = 0; j < graphs.count(); j++)
1144 pi->addGraph(graphs.at(j).at(i));
1146 if (gt) {
1147 pi->setGraph(_tabs.indexOf(gt));
1148 pi->setMarkerPosition(gt->sliderPosition());
1152 updateDEMDownloadAction();
1155 void GUI::openPOIFile()
1157 #ifdef Q_OS_ANDROID
1158 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"),
1159 _poiDir));
1160 #else // Q_OS_ANDROID
1161 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"),
1162 _poiDir, Data::formats()));
1163 #endif // Q_OS_ANDROID
1165 for (int i = 0; i < files.size(); i++)
1166 openPOIFile(files.at(i));
1167 if (!files.isEmpty())
1168 _poiDir = QFileInfo(files.last()).path();
1171 bool GUI::openPOIFile(const QString &fileName)
1173 if (_poi->isLoaded(fileName))
1174 return true;
1176 if (_poi->loadFile(fileName)) {
1177 _mapView->showPOI(true);
1178 _showPOIAction->setChecked(true);
1179 QAction *action = new POIAction(fileName, _poisActionGroup);
1180 action->setChecked(true);
1181 _poiMenu->insertAction(_poisEnd, action);
1183 _selectAllPOIAction->setEnabled(true);
1184 _unselectAllPOIAction->setEnabled(true);
1186 return true;
1187 } else {
1188 QString error = tr("Error loading POI file:") + "\n"
1189 + Util::displayName(fileName) + ": " + _poi->errorString();
1190 if (_poi->errorLine())
1191 error.append("\n" + tr("Line: %1").arg(_poi->errorLine()));
1192 QMessageBox::critical(this, APP_NAME, error);
1194 return false;
1198 void GUI::openOptions()
1200 Options options(_options);
1201 OptionsDialog dialog(options, _units, this);
1203 if (dialog.exec() != QDialog::Accepted)
1204 return;
1206 updateOptions(options);
1209 void GUI::printFile()
1211 QPrinter printer(QPrinter::HighResolution);
1212 QPrintDialog dialog(&printer, this);
1214 if (dialog.exec() == QDialog::Accepted)
1215 plot(&printer);
1218 void GUI::exportPDFFile()
1220 PDFExportDialog dialog(_pdfExport, _units, this);
1221 if (dialog.exec() != QDialog::Accepted)
1222 return;
1224 QPrinter printer(QPrinter::HighResolution);
1225 printer.setOutputFormat(QPrinter::PdfFormat);
1226 printer.setCreator(QString(APP_NAME) + QString(" ")
1227 + QString(APP_VERSION));
1228 printer.setResolution(_pdfExport.resolution);
1229 printer.setPageLayout(QPageLayout(QPageSize(_pdfExport.paperSize),
1230 _pdfExport.orientation, _pdfExport.margins, QPageLayout::Millimeter));
1231 printer.setOutputFileName(_pdfExport.fileName);
1233 plot(&printer);
1236 void GUI::exportPNGFile()
1238 PNGExportDialog dialog(_pngExport, this);
1239 if (dialog.exec() != QDialog::Accepted)
1240 return;
1242 QImage img(_pngExport.size, QImage::Format_ARGB32_Premultiplied);
1243 QPainter p(&img);
1244 QRectF rect(0, 0, img.width(), img.height());
1245 QRectF contentRect(rect.adjusted(_pngExport.margins.left(),
1246 _pngExport.margins.top(), -_pngExport.margins.right(),
1247 -_pngExport.margins.bottom()));
1249 if (_pngExport.antialiasing)
1250 p.setRenderHint(QPainter::Antialiasing);
1251 p.fillRect(rect, Qt::white);
1252 plotMainPage(&p, contentRect, 1.0, true);
1253 img.save(_pngExport.fileName, "png");
1255 if (!_tabs.isEmpty() && _options.separateGraphPage) {
1256 QImage img2(_pngExport.size.width(), (int)graphPlotHeight(rect, 1)
1257 + _pngExport.margins.bottom(), QImage::Format_ARGB32_Premultiplied);
1258 QPainter p2(&img2);
1259 QRectF rect2(0, 0, img2.width(), img2.height());
1261 if (_pngExport.antialiasing)
1262 p2.setRenderHint(QPainter::Antialiasing);
1263 p2.fillRect(rect2, Qt::white);
1264 plotGraphsPage(&p2, contentRect, 1);
1266 QFileInfo fi(_pngExport.fileName);
1267 img2.save(fi.absolutePath() + "/" + fi.baseName() + "-graphs."
1268 + fi.suffix(), "png");
1272 void GUI::statistics()
1274 QLocale l(QLocale::system());
1275 QMessageBox msgBox(this);
1276 QString text;
1278 #ifdef Q_OS_ANDROID
1279 if (_showTracksAction->isChecked() && _trackCount > 1)
1280 text.append("<b>" + tr("Tracks") + ":</b> "
1281 + l.toString(_trackCount) + "<br>");
1282 if (_showRoutesAction->isChecked() && _routeCount > 1)
1283 text.append("<b>" + tr("Routes") + ":</b> "
1284 + l.toString(_routeCount) + "<br>");
1285 if (_showWaypointsAction->isChecked() && _waypointCount > 1)
1286 text.append("<b>" + tr("Waypoints") + ":</b> "
1287 + l.toString(_waypointCount) + "<br>");
1288 if (_showAreasAction->isChecked() && _areaCount > 1)
1289 text.append("<b>" + tr("Areas") + ":</b> "
1290 + l.toString(_areaCount) + "<br>");
1292 if (_dateRange.first.isValid()) {
1293 QString format = l.dateFormat(QLocale::ShortFormat);
1294 if (_dateRange.first == _dateRange.second)
1295 text.append("<b>" + tr("Date") + ":</b> "
1296 + _dateRange.first.toString(format) + "<br>");
1297 else
1298 text.append("<b>" + tr("Date") + ":</b> "
1299 + QString("%1 - %2").arg(_dateRange.first.toString(format),
1300 _dateRange.second.toString(format)) + "<br>");
1303 if (distance() > 0)
1304 text.append("<b>" + tr("Distance") + ":</b> "
1305 + Format::distance(distance(), units()) + "<br>");
1306 if (time() > 0) {
1307 text.append("<b>" + tr("Time") + ":</b> "
1308 + Format::timeSpan(time()) + "<br>");
1309 text.append("<b>" + tr("Moving time") + ":</b> "
1310 + Format::timeSpan(movingTime()) + "<br>");
1312 text.append("<br>");
1314 for (int i = 0; i < _tabs.count(); i++) {
1315 const GraphTab *tab = _tabs.at(i);
1316 if (tab->isEmpty())
1317 continue;
1319 text.append("<i>" + tab->label() + "</i><br>");
1320 for (int j = 0; j < tab->info().size(); j++) {
1321 const KV<QString, QString> &kv = tab->info().at(j);
1322 text.append("<b>" + kv.key() + ":</b>&nbsp;" + kv.value());
1323 if (j != tab->info().size() - 1)
1324 text.append(" | ");
1326 if (i != _tabs.count() - 1)
1327 text.append("<br><br>");
1330 msgBox.setWindowTitle(tr("Statistics"));
1331 msgBox.setText(text);
1333 #else // Q_OS_ANDROID
1335 #ifdef Q_OS_WIN32
1336 text = "<style>td {white-space: pre; padding-right: 4em;}"
1337 "th {text-align: left; padding-top: 0.5em;}</style><table>";
1338 #else // Q_OS_WIN32
1339 text = "<style>td {white-space: pre; padding-right: 2em;}"
1340 "th {text-align: left; padding-top: 0.5em;}</style><table>";
1341 #endif // Q_OS_WIN32
1343 if (_showTracksAction->isChecked() && _trackCount > 1)
1344 text.append("<tr><td>" + tr("Tracks") + ":</td><td>"
1345 + l.toString(_trackCount) + "</td></tr>");
1346 if (_showRoutesAction->isChecked() && _routeCount > 1)
1347 text.append("<tr><td>" + tr("Routes") + ":</td><td>"
1348 + l.toString(_routeCount) + "</td></tr>");
1349 if (_showWaypointsAction->isChecked() && _waypointCount > 1)
1350 text.append("<tr><td>" + tr("Waypoints") + ":</td><td>"
1351 + l.toString(_waypointCount) + "</td></tr>");
1352 if (_showAreasAction->isChecked() && _areaCount > 1)
1353 text.append("<tr><td>" + tr("Areas") + ":</td><td>"
1354 + l.toString(_areaCount) + "</td></tr>");
1356 if (_dateRange.first.isValid()) {
1357 if (_dateRange.first == _dateRange.second) {
1358 QString format = l.dateFormat(QLocale::LongFormat);
1359 text.append("<tr><td>" + tr("Date") + ":</td><td>"
1360 + _dateRange.first.toString(format) + "</td></tr>");
1361 } else {
1362 QString format = l.dateFormat(QLocale::ShortFormat);
1363 text.append("<tr><td>" + tr("Date") + ":</td><td>"
1364 + QString("%1 - %2").arg(_dateRange.first.toString(format),
1365 _dateRange.second.toString(format)) + "</td></tr>");
1369 if (distance() > 0)
1370 text.append("<tr><td>" + tr("Distance") + ":</td><td>"
1371 + Format::distance(distance(), units()) + "</td></tr>");
1372 if (time() > 0) {
1373 text.append("<tr><td>" + tr("Time") + ":</td><td>"
1374 + Format::timeSpan(time()) + "</td></tr>");
1375 text.append("<tr><td>" + tr("Moving time") + ":</td><td>"
1376 + Format::timeSpan(movingTime()) + "</td></tr>");
1379 for (int i = 0; i < _tabs.count(); i++) {
1380 const GraphTab *tab = _tabs.at(i);
1381 if (tab->isEmpty())
1382 continue;
1384 text.append("<tr><th colspan=\"2\">" + tab->label() + "</th></tr>");
1385 for (int j = 0; j < tab->info().size(); j++) {
1386 const KV<QString, QString> &kv = tab->info().at(j);
1387 text.append("<tr><td>" + kv.key() + ":</td><td>" + kv.value()
1388 + "</td></tr>");
1392 text.append("</table>");
1394 msgBox.setWindowTitle(tr("Statistics"));
1395 msgBox.setText("<h3>" + tr("Statistics") + "</h3>");
1396 msgBox.setInformativeText(text);
1397 #endif // Q_OS_ANDROID
1399 msgBox.exec();
1402 void GUI::plotMainPage(QPainter *painter, const QRectF &rect, qreal ratio,
1403 bool expand)
1405 QLocale l(QLocale::system());
1406 TrackInfo info;
1407 qreal ih, gh, mh;
1408 int sc;
1411 if (!_pathName.isNull() && _options.printName)
1412 info.insert(tr("Name"), _pathName);
1414 if (_options.printItemCount) {
1415 if (_showTracksAction->isChecked() && _trackCount > 1)
1416 info.insert(tr("Tracks"), l.toString(_trackCount));
1417 if (_showRoutesAction->isChecked() && _routeCount > 1)
1418 info.insert(tr("Routes"), l.toString(_routeCount));
1419 if (_showWaypointsAction->isChecked() && _waypointCount > 1)
1420 info.insert(tr("Waypoints"), l.toString(_waypointCount));
1421 if (_showAreasAction->isChecked() && _areaCount > 1)
1422 info.insert(tr("Areas"), l.toString(_areaCount));
1425 if (_dateRange.first.isValid() && _options.printDate) {
1426 if (_dateRange.first == _dateRange.second) {
1427 QString format = l.dateFormat(QLocale::LongFormat);
1428 info.insert(tr("Date"), _dateRange.first.toString(format));
1429 } else {
1430 QString format = l.dateFormat(QLocale::ShortFormat);
1431 info.insert(tr("Date"), QString("%1 - %2")
1432 .arg(_dateRange.first.toString(format),
1433 _dateRange.second.toString(format)));
1437 if (distance() > 0 && _options.printDistance)
1438 info.insert(tr("Distance"), Format::distance(distance(), units()));
1439 if (time() > 0 && _options.printTime)
1440 info.insert(tr("Time"), Format::timeSpan(time()));
1441 if (movingTime() > 0 && _options.printMovingTime)
1442 info.insert(tr("Moving time"), Format::timeSpan(movingTime()));
1444 if (info.isEmpty()) {
1445 ih = 0;
1446 mh = 0;
1447 } else {
1448 ih = info.contentSize().height() * ratio;
1449 mh = ih / 2;
1450 info.plot(painter, QRectF(rect.x(), rect.y(), rect.width(), ih), ratio);
1452 if (_graphTabWidget->isVisible() && !_options.separateGraphPage) {
1453 qreal r = rect.width() / rect.height();
1454 gh = (rect.width() > rect.height())
1455 ? 0.15 * r * (rect.height() - ih - 2*mh)
1456 : 0.15 * (rect.height() - ih - 2*mh);
1457 if (gh < 150)
1458 gh = 150;
1459 sc = 2;
1460 GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->currentWidget());
1461 gt->plot(painter, QRectF(rect.x(), rect.y() + rect.height() - gh,
1462 rect.width(), gh), ratio);
1463 } else {
1464 gh = 0;
1465 sc = 1;
1468 MapView::PlotFlags flags;
1469 if (_options.hiresPrint)
1470 flags |= MapView::HiRes;
1471 if (expand)
1472 flags |= MapView::Expand;
1474 _mapView->plot(painter, QRectF(rect.x(), rect.y() + ih + mh, rect.width(),
1475 rect.height() - (ih + sc*mh + gh)), ratio, flags);
1478 void GUI::plotGraphsPage(QPainter *painter, const QRectF &rect, qreal ratio)
1480 int cnt = 0;
1481 for (int i = 0; i < _tabs.size(); i++)
1482 if (!_tabs.at(i)->isEmpty())
1483 cnt++;
1485 qreal sp = ratio * 20;
1486 qreal gh = qMin((rect.height() - ((cnt - 1) * sp))/(qreal)cnt,
1487 0.20 * rect.height());
1489 qreal y = 0;
1490 for (int i = 0; i < _tabs.size(); i++) {
1491 if (!_tabs.at(i)->isEmpty()) {
1492 _tabs.at(i)->plot(painter, QRectF(rect.x(), rect.y() + y,
1493 rect.width(), gh), ratio);
1494 y += gh + sp;
1499 qreal GUI::graphPlotHeight(const QRectF &rect, qreal ratio)
1501 int cnt = 0;
1502 for (int i = 0; i < _tabs.size(); i++)
1503 if (!_tabs.at(i)->isEmpty())
1504 cnt++;
1506 qreal sp = ratio * 20;
1507 qreal gh = qMin((rect.height() - ((cnt - 1) * sp))/(qreal)cnt,
1508 0.20 * rect.height());
1510 return cnt * gh + (cnt - 1) * sp;
1513 void GUI::plot(QPrinter *printer)
1515 QPainter p(printer);
1516 qreal fsr = 1085.0 / (qMax(printer->width(), printer->height())
1517 / (qreal)printer->resolution());
1518 qreal ratio = p.paintEngine()->paintDevice()->logicalDpiX() / fsr;
1519 QRectF rect(0, 0, printer->width(), printer->height());
1521 plotMainPage(&p, rect, ratio);
1523 if (!_tabs.isEmpty() && _options.separateGraphPage) {
1524 printer->newPage();
1525 plotGraphsPage(&p, rect, ratio);
1529 void GUI::reloadFiles()
1531 _trackCount = 0;
1532 _routeCount = 0;
1533 _waypointCount = 0;
1534 _areaCount = 0;
1535 _trackDistance = 0;
1536 _routeDistance = 0;
1537 _time = 0;
1538 _movingTime = 0;
1539 _dateRange = DateTimeRange(QDateTime(), QDateTime());
1540 _pathName = QString();
1542 for (int i = 0; i < _tabs.count(); i++)
1543 _tabs.at(i)->clear();
1544 _mapView->clear();
1546 int showError = 2;
1547 for (int i = 0; i < _files.size(); i++) {
1548 if (!loadFile(_files.at(i), true, showError)) {
1549 _files.removeAt(i);
1550 i--;
1554 updateStatusBarInfo();
1555 updateWindowTitle();
1556 if (_files.isEmpty())
1557 _fileActionGroup->setEnabled(false);
1558 #ifndef Q_OS_ANDROID
1559 else
1560 _browser->setCurrent(_files.last());
1561 #endif // Q_OS_ANDROID
1562 updateDEMDownloadAction();
1565 void GUI::closeFiles()
1567 _trackCount = 0;
1568 _routeCount = 0;
1569 _waypointCount = 0;
1570 _areaCount = 0;
1571 _trackDistance = 0;
1572 _routeDistance = 0;
1573 _time = 0;
1574 _movingTime = 0;
1575 _dateRange = DateTimeRange(QDateTime(), QDateTime());
1576 _pathName = QString();
1578 for (int i = 0; i < _tabs.count(); i++)
1579 _tabs.at(i)->clear();
1580 _lastTab = 0;
1582 _mapView->clear();
1584 _files.clear();
1587 void GUI::closeAll()
1589 closeFiles();
1591 _fileActionGroup->setEnabled(false);
1592 updateStatusBarInfo();
1593 updateWindowTitle();
1594 updateGraphTabs();
1595 updateDEMDownloadAction();
1597 #ifdef Q_OS_ANDROID
1598 _browser->setCurrentDir(QString());
1599 #endif // Q_OS_ANDROID
1602 void GUI::showGraphs(bool show)
1604 _graphTabWidget->setHidden(!show);
1607 #ifdef Q_OS_ANDROID
1608 void GUI::showGraphTabs(bool show)
1610 _graphTabWidget->tabBar()->setVisible(show);
1612 #else // Q_OS_ANDROID
1613 void GUI::showToolbars(bool show)
1615 if (show) {
1616 Q_ASSERT(!_windowStates.isEmpty());
1617 restoreState(_windowStates.last());
1618 _windowStates.removeLast();
1619 } else {
1620 _windowStates.append(saveState());
1621 removeToolBar(_fileToolBar);
1622 removeToolBar(_showToolBar);
1623 removeToolBar(_navigationToolBar);
1627 void GUI::showFullscreen(bool show)
1629 if (show) {
1630 _windowGeometries.append(saveGeometry());
1631 _frameStyle = _mapView->frameStyle();
1632 statusBar()->hide();
1633 menuBar()->hide();
1634 showToolbars(false);
1635 _mapView->setFrameStyle(QFrame::NoFrame);
1636 _graphTabWidget->tabBar()->hide();
1637 #ifdef Q_OS_MAC
1638 _graphTabWidget->setDocumentMode(true);
1639 #endif // Q_OS_MAC
1640 showFullScreen();
1641 } else {
1642 Q_ASSERT(!_windowGeometries.isEmpty());
1643 _windowGeometries.removeLast();
1644 statusBar()->show();
1645 menuBar()->show();
1646 showToolbars(true);
1647 _mapView->setFrameStyle(_frameStyle);
1648 _graphTabWidget->tabBar()->show();
1649 #ifdef Q_OS_MAC
1650 _graphTabWidget->setDocumentMode(false);
1651 #endif // Q_OS_MAC
1652 showNormal();
1655 #endif // Q_OS_ANDROID
1657 void GUI::showTracks(bool show)
1659 _mapView->showTracks(show);
1661 for (int i = 0; i < _tabs.size(); i++)
1662 _tabs.at(i)->showTracks(show);
1664 updateStatusBarInfo();
1665 updateGraphTabs();
1666 updateDEMDownloadAction();
1669 void GUI::showRoutes(bool show)
1671 _mapView->showRoutes(show);
1673 for (int i = 0; i < _tabs.size(); i++)
1674 _tabs.at(i)->showRoutes(show);
1676 updateStatusBarInfo();
1677 updateGraphTabs();
1678 updateDEMDownloadAction();
1681 void GUI::showWaypoints(bool show)
1683 _mapView->showWaypoints(show);
1684 updateDEMDownloadAction();
1687 void GUI::showAreas(bool show)
1689 _mapView->showAreas(show);
1690 updateDEMDownloadAction();
1693 void GUI::showGraphGrids(bool show)
1695 for (int i = 0; i < _tabs.size(); i++)
1696 _tabs.at(i)->showGrid(show);
1699 void GUI::showGraphSliderInfo(bool show)
1701 for (int i = 0; i < _tabs.size(); i++)
1702 _tabs.at(i)->showSliderInfo(show);
1705 void GUI::showPathMarkerInfo(QAction *action)
1707 if (action == _showMarkersAction) {
1708 _mapView->showMarkers(true);
1709 _mapView->showMarkerInfo(MarkerInfoItem::None);
1710 } else if (action == _showMarkerDateAction) {
1711 _mapView->showMarkers(true);
1712 _mapView->showMarkerInfo(MarkerInfoItem::Date);
1713 } else if (action == _showMarkerCoordinatesAction) {
1714 _mapView->showMarkers(true);
1715 _mapView->showMarkerInfo(MarkerInfoItem::Position);
1716 } else {
1717 _mapView->showMarkers(false);
1718 _mapView->showMarkerInfo(MarkerInfoItem::None);
1722 void GUI::loadMap()
1724 #ifdef Q_OS_ANDROID
1725 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"),
1726 _mapDir));
1727 #else // Q_OS_ANDROID
1728 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"),
1729 _mapDir, MapList::formats()));
1730 #endif // Q_OS_ANDROID
1731 MapAction *a, *lastReady = 0;
1732 int showError = (files.size() > 1) ? 2 : 1;
1734 for (int i = 0; i < files.size(); i++) {
1735 if (loadMap(files.at(i), a, showError) && a)
1736 lastReady = a;
1738 if (!files.isEmpty())
1739 _mapDir = QFileInfo(files.last()).path();
1740 if (lastReady)
1741 lastReady->trigger();
1744 static MapAction *findMapAction(const QList<QAction*> &mapActions,
1745 const Map *map)
1747 for (int i = 0; i < mapActions.count(); i++) {
1748 const Map *m = mapActions.at(i)->data().value<Map*>();
1749 if (map->path() == m->path())
1750 return static_cast<MapAction*>(mapActions.at(i));
1753 return 0;
1756 bool GUI::loadMapNode(const TreeNode<Map*> &node, MapAction *&action,
1757 const QList<QAction*> &existingActions, int &showError)
1759 bool valid = false;
1761 action = 0;
1763 for (int i = 0; i < node.childs().size(); i++)
1764 valid = loadMapNode(node.childs().at(i), action, existingActions,
1765 showError);
1767 for (int i = 0; i < node.items().size(); i++) {
1768 Map *map = node.items().at(i);
1769 MapAction *a;
1771 if (!(a = findMapAction(existingActions, map))) {
1772 if (!map->isValid()) {
1773 if (showError) {
1774 QString error = tr("Error loading map:") + "\n"
1775 + Util::displayName(map->path()) + ": " + map->errorString();
1777 if (showError > 1) {
1778 QMessageBox message(QMessageBox::Critical, APP_NAME,
1779 error, QMessageBox::Ok, this);
1780 QCheckBox checkBox(tr("Don't show again"));
1781 message.setCheckBox(&checkBox);
1782 message.exec();
1783 if (checkBox.isChecked())
1784 showError = 0;
1785 } else
1786 QMessageBox::critical(this, APP_NAME, error);
1789 delete map;
1790 } else {
1791 valid = true;
1792 a = new MapAction(map, _mapsActionGroup);
1793 _mapMenu->insertAction(_mapsEnd, a);
1795 if (map->isReady()) {
1796 action = a;
1797 _showMapAction->setEnabled(true);
1798 _clearMapCacheAction->setEnabled(true);
1799 } else
1800 connect(a, &MapAction::loaded, this, &GUI::mapLoaded);
1802 } else {
1803 valid = true;
1804 map = a->data().value<Map*>();
1805 if (map->isReady())
1806 action = a;
1810 return valid;
1813 bool GUI::loadMap(const QString &fileName, MapAction *&action, int &showError)
1815 TreeNode<Map*> maps(MapList::loadMaps(fileName, _mapView->inputProjection()));
1816 QList<QAction*> existingActions(_mapsActionGroup->actions());
1818 return loadMapNode(maps, action, existingActions, showError);
1821 void GUI::mapLoaded()
1823 MapAction *action = static_cast<MapAction*>(QObject::sender());
1824 Map *map = action->data().value<Map*>();
1826 if (map->isValid()) {
1827 action->trigger();
1828 _showMapAction->setEnabled(true);
1829 _clearMapCacheAction->setEnabled(true);
1830 } else {
1831 QString error = tr("Error loading map:") + "\n"
1832 + Util::displayName(map->path()) + ": " + map->errorString();
1833 QMessageBox::critical(this, APP_NAME, error);
1834 action->deleteLater();
1838 void GUI::mapLoadedDir()
1840 MapAction *action = static_cast<MapAction*>(QObject::sender());
1841 Map *map = action->data().value<Map*>();
1843 if (map->isValid()) {
1844 _showMapAction->setEnabled(true);
1845 _clearMapCacheAction->setEnabled(true);
1846 QList<MapAction*> actions;
1847 actions.append(action);
1848 _mapView->loadMaps(actions);
1849 } else {
1850 QString error = tr("Error loading map:") + "\n"
1851 + Util::displayName(map->path()) + ": " + map->errorString();
1852 QMessageBox::critical(this, APP_NAME, error);
1853 action->deleteLater();
1857 void GUI::loadMapDirNode(const TreeNode<Map *> &node, QList<MapAction*> &actions,
1858 QMenu *menu, const QList<QAction*> &existingActions, int &showError)
1860 for (int i = 0; i < node.childs().size(); i++) {
1861 QMenu *cm = new QMenu(node.childs().at(i).name(), menu);
1862 menu->addMenu(cm);
1863 loadMapDirNode(node.childs().at(i), actions, cm, existingActions,
1864 showError);
1867 for (int i = 0; i < node.items().size(); i++) {
1868 Map *map = node.items().at(i);
1869 MapAction *a;
1871 if (!(a = findMapAction(existingActions, map))) {
1872 if (!map->isValid()) {
1873 if (showError) {
1874 QString error = tr("Error loading map:") + "\n"
1875 + Util::displayName(map->path()) + ": " + map->errorString();
1877 if (showError > 1) {
1878 QMessageBox message(QMessageBox::Critical, APP_NAME,
1879 error, QMessageBox::Ok, this);
1880 QCheckBox checkBox(tr("Don't show again"));
1881 message.setCheckBox(&checkBox);
1882 message.exec();
1883 if (checkBox.isChecked())
1884 showError = 0;
1885 } else
1886 QMessageBox::critical(this, APP_NAME, error);
1889 delete map;
1890 } else {
1891 a = new MapAction(map, _mapsActionGroup);
1892 menu->addAction(a);
1894 if (map->isReady()) {
1895 _showMapAction->setEnabled(true);
1896 _clearMapCacheAction->setEnabled(true);
1897 actions.append(a);
1898 } else
1899 connect(a, &MapAction::loaded, this, &GUI::mapLoadedDir);
1901 _areaCount++;
1903 } else {
1904 map = a->data().value<Map*>();
1905 if (map->isReady())
1906 actions.append(a);
1911 void GUI::loadMapDir()
1913 QString dir(QFileDialog::getExistingDirectory(this,
1914 tr("Select map directory"), _mapDir, QFileDialog::ShowDirsOnly));
1915 if (dir.isEmpty())
1916 return;
1918 QFileInfo fi(dir);
1919 TreeNode<Map*> maps(MapList::loadMaps(dir, _mapView->inputProjection()));
1920 QList<QAction*> existingActions(_mapsActionGroup->actions());
1921 QList<MapAction*> actions;
1922 QMenu *menu = new QMenu(maps.name());
1923 int showError = (maps.items().size() > 1 || !maps.childs().isEmpty())
1924 ? 2 : 1;
1926 loadMapDirNode(maps, actions, menu, existingActions, showError);
1928 _mapView->loadMaps(actions);
1930 if (menu->isEmpty())
1931 delete menu;
1932 else
1933 _mapMenu->insertMenu(_mapsEnd, menu);
1935 _mapDir = fi.absolutePath();
1936 _fileActionGroup->setEnabled(true);
1937 _reloadFileAction->setEnabled(false);
1940 void GUI::clearMapCache()
1942 if (QMessageBox::question(this, APP_NAME,
1943 tr("Clear \"%1\" tile cache?").arg(_map->name())) == QMessageBox::Yes)
1944 _mapView->clearMapCache();
1947 void GUI::downloadDEM()
1949 RectC br(_mapView->boundingRect());
1950 _demRects.append(br);
1952 if (!_dem->loadTiles(br) && _demRects.size() == 1)
1953 demLoaded();
1956 void GUI::demLoaded()
1958 for (int i = 0; i < _demRects.size(); i++) {
1959 if (!_dem->checkTiles(_demRects.at(i))) {
1960 QMessageBox::warning(this, APP_NAME,
1961 tr("Could not download all required DEM files."));
1962 break;
1966 DEM::clearCache();
1967 _demRects.clear();
1968 reloadFiles();
1971 void GUI::showDEMTiles()
1973 QList<Area> tiles(DEM::tiles());
1975 if (tiles.isEmpty()) {
1976 QMessageBox::information(this, APP_NAME, tr("No local DEM tiles found."));
1977 } else {
1978 _mapView->loadDEMs(tiles);
1980 _areaCount += tiles.size();
1982 _fileActionGroup->setEnabled(true);
1983 _reloadFileAction->setEnabled(false);
1987 void GUI::updateStatusBarInfo()
1989 if (_files.count() == 0)
1990 _fileNameLabel->setText(tr("No files loaded"));
1991 else if (_files.count() == 1)
1992 _fileNameLabel->setText(Util::displayName(_files.at(0)));
1993 else
1994 _fileNameLabel->setText(tr("%n files", "", _files.count()));
1996 if (distance() > 0)
1997 _distanceLabel->setText(Format::distance(distance(), units()));
1998 else
1999 _distanceLabel->clear();
2001 if (time() > 0) {
2002 if (_movingTimeAction->isChecked()) {
2003 _timeLabel->setText(Format::timeSpan(movingTime())
2004 + "<sub>M</sub>");
2005 _timeLabel->setToolTip(Format::timeSpan(time()));
2006 } else {
2007 _timeLabel->setText(Format::timeSpan(time()));
2008 _timeLabel->setToolTip(Format::timeSpan(movingTime())
2009 + "<sub>M</sub>");
2011 } else {
2012 _timeLabel->clear();
2013 _timeLabel->setToolTip(QString());
2016 #ifdef Q_OS_ANDROID
2017 statusBar()->setVisible(!_files.isEmpty());
2018 #endif // Q_OS_ANDROID
2021 void GUI::updateWindowTitle()
2023 if (_files.count() == 1)
2024 setWindowTitle(QFileInfo(_files.at(0)).fileName() + " - " + APP_NAME);
2025 else
2026 setWindowTitle(APP_NAME);
2029 #ifndef Q_OS_ANDROID
2030 void GUI::updateRecentFiles(const QString &fileName)
2032 QAction *a = 0;
2034 QList<QAction *> actions(_recentFilesActionGroup->actions());
2035 for (int i = 0; i < actions.size(); i++) {
2036 if (actions.at(i)->text() == fileName) {
2037 a = actions.at(i);
2038 break;
2042 if (a)
2043 delete a;
2044 else if (actions.size() == MAX_RECENT_FILES)
2045 delete actions.last();
2047 actions = _recentFilesActionGroup->actions();
2048 QAction *before = actions.size() ? actions.last() : _recentFilesEnd;
2049 _recentFilesMenu->insertAction(before,
2050 new QAction(fileName, _recentFilesActionGroup));
2051 _recentFilesMenu->setEnabled(true);
2054 void GUI::clearRecentFiles()
2056 QList<QAction *> actions(_recentFilesActionGroup->actions());
2058 for (int i = 0; i < actions.size(); i++)
2059 delete actions.at(i);
2061 _recentFilesMenu->setEnabled(false);
2064 void GUI::recentFileSelected(QAction *action)
2066 int showError = 1;
2067 openFile(action->text(), true, showError);
2069 #endif // Q_OS_ANDROID
2071 void GUI::mapChanged(QAction *action)
2073 _map = action->data().value<Map*>();
2074 _mapView->setMap(_map);
2077 void GUI::nextMap()
2079 QAction *checked = _mapsActionGroup->checkedAction();
2080 if (!checked)
2081 return;
2083 QList<QAction*> maps(_mapsActionGroup->actions());
2084 for (int i = 1; i < maps.size(); i++) {
2085 int next = (maps.indexOf(checked) + i) % maps.count();
2086 if (maps.at(next)->isEnabled()) {
2087 maps.at(next)->trigger();
2088 break;
2093 void GUI::prevMap()
2095 QAction *checked = _mapsActionGroup->checkedAction();
2096 if (!checked)
2097 return;
2099 QList<QAction*> maps(_mapsActionGroup->actions());
2100 for (int i = 1; i < maps.size(); i++) {
2101 int prev = (maps.indexOf(checked) + maps.count() - i) % maps.count();
2102 if (maps.at(prev)->isEnabled()) {
2103 maps.at(prev)->trigger();
2104 break;
2109 void GUI::poiFileChecked(QAction *action)
2111 _poi->enableFile(action->data().value<QString>(), action->isChecked());
2114 void GUI::selectAllPOIs()
2116 QList<QAction*> actions(_poisActionGroup->actions());
2117 for (int i = 0; i < actions.size(); i++) {
2118 POIAction *a = static_cast<POIAction*>(actions.at(i));
2119 if (_poi->enableFile(a->data().toString(), true))
2120 a->setChecked(true);
2124 void GUI::unselectAllPOIs()
2126 QList<QAction*> actions(_poisActionGroup->actions());
2127 for (int i = 0; i < actions.size(); i++) {
2128 POIAction *a = static_cast<POIAction*>(actions.at(i));
2129 if (_poi->enableFile(a->data().toString(), false))
2130 a->setChecked(false);
2134 void GUI::graphChanged(int index)
2136 if (index < 0)
2137 return;
2139 GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->widget(index));
2141 _mapView->setGraph(_tabs.indexOf(gt));
2143 if (_lastTab)
2144 gt->setSliderPosition(_lastTab->sliderPosition());
2145 _lastTab = gt;
2148 void GUI::updateNavigationActions()
2150 #ifdef Q_OS_ANDROID
2151 _navigation->enableNext(!_browser->isLast()
2152 && !_browser->current().isNull());
2153 _navigation->enablePrev(!_browser->isFirst()
2154 && !_browser->current().isNull());
2155 #else // Q_OS_ANDROID
2156 _lastAction->setEnabled(!_browser->isLast());
2157 _nextAction->setEnabled(!_browser->isLast());
2158 _firstAction->setEnabled(!_browser->isFirst());
2159 _prevAction->setEnabled(!_browser->isFirst());
2160 #endif // Q_OS_ANDROID
2163 bool GUI::updateGraphTabs()
2165 int index;
2166 GraphTab *tab;
2167 bool hidden = _graphTabWidget->isHidden();
2169 for (int i = 0; i < _tabs.size(); i++) {
2170 tab = _tabs.at(i);
2171 if (tab->isEmpty() && (index = _graphTabWidget->indexOf(tab)) >= 0)
2172 _graphTabWidget->removeTab(index);
2175 for (int i = 0; i < _tabs.size(); i++) {
2176 tab = _tabs.at(i);
2177 if (!tab->isEmpty() && _graphTabWidget->indexOf(tab) < 0)
2178 _graphTabWidget->insertTab(i, tab, _tabs.at(i)->label());
2181 if (_graphTabWidget->count() &&
2182 ((_showTracksAction->isChecked() && _trackCount)
2183 || (_showRoutesAction->isChecked() && _routeCount))) {
2184 if (_showGraphsAction->isChecked())
2185 _graphTabWidget->setHidden(false);
2186 _showGraphsAction->setEnabled(true);
2187 } else {
2188 _graphTabWidget->setHidden(true);
2189 _showGraphsAction->setEnabled(false);
2192 return (hidden != _graphTabWidget->isHidden());
2195 void GUI::updateDEMDownloadAction()
2197 _downloadDEMAction->setEnabled(!_dem->url().isEmpty()
2198 && !_dem->checkTiles(_mapView->boundingRect()));
2201 void GUI::setTimeType(TimeType type)
2203 for (int i = 0; i <_tabs.count(); i++)
2204 _tabs.at(i)->setTimeType(type);
2206 updateStatusBarInfo();
2209 void GUI::setUnits(Units units)
2211 _units = units;
2213 _mapView->setUnits(units);
2214 for (int i = 0; i <_tabs.count(); i++)
2215 _tabs.at(i)->setUnits(units);
2216 updateStatusBarInfo();
2219 void GUI::setCoordinatesFormat(CoordinatesFormat format)
2221 _mapView->setCoordinatesFormat(format);
2224 void GUI::setGraphType(GraphType type)
2226 for (int i = 0; i <_tabs.count(); i++)
2227 _tabs.at(i)->setGraphType(type);
2230 void GUI::next()
2232 int showError = 1;
2233 QString file = _browser->next();
2234 if (file.isNull())
2235 return;
2237 closeFiles();
2238 openFile(file, true, showError);
2241 void GUI::prev()
2243 int showError = 1;
2244 QString file = _browser->prev();
2245 if (file.isNull())
2246 return;
2248 closeFiles();
2249 openFile(file, true, showError);
2252 void GUI::last()
2254 int showError = 1;
2255 QString file = _browser->last();
2256 if (file.isNull())
2257 return;
2259 closeFiles();
2260 openFile(file, true, showError);
2263 void GUI::first()
2265 int showError = 1;
2266 QString file = _browser->first();
2267 if (file.isNull())
2268 return;
2270 closeFiles();
2271 openFile(file, true, showError);
2274 #ifndef Q_OS_ANDROID
2275 void GUI::keyPressEvent(QKeyEvent *event)
2277 QString file;
2278 int showError = 1;
2280 switch (event->key()) {
2281 case PREV_KEY:
2282 file = _browser->prev();
2283 break;
2284 case NEXT_KEY:
2285 file = _browser->next();
2286 break;
2287 case FIRST_KEY:
2288 file = _browser->first();
2289 break;
2290 case LAST_KEY:
2291 file = _browser->last();
2292 break;
2294 case TOGGLE_GRAPH_TYPE_KEY:
2295 if (_timeGraphAction->isChecked())
2296 _distanceGraphAction->trigger();
2297 else
2298 _timeGraphAction->trigger();
2299 break;
2300 case TOGGLE_TIME_TYPE_KEY:
2301 if (_movingTimeAction->isChecked())
2302 _totalTimeAction->trigger();
2303 else
2304 _movingTimeAction->trigger();
2305 break;
2306 case TOGGLE_MARKER_INFO_KEY:
2307 if (_showMarkerDateAction->isChecked())
2308 _showMarkerCoordinatesAction->trigger();
2309 else if (_showMarkerCoordinatesAction->isChecked())
2310 _showMarkerDateAction->trigger();
2311 break;
2312 case Qt::Key_Escape:
2313 if (_fullscreenAction->isChecked()) {
2314 _fullscreenAction->setChecked(false);
2315 showFullscreen(false);
2316 return;
2318 break;
2321 if (!file.isNull()) {
2322 if (!(event->modifiers() & MODIFIER))
2323 closeFiles();
2324 openFile(file, true, showError);
2325 return;
2328 QMainWindow::keyPressEvent(event);
2330 #endif // Q_OS_ANDROID
2332 void GUI::closeEvent(QCloseEvent *event)
2334 writeSettings();
2335 QMainWindow::closeEvent(event);
2338 void GUI::dragEnterEvent(QDragEnterEvent *event)
2340 if (!event->mimeData()->hasUrls())
2341 return;
2342 if (event->proposedAction() != Qt::CopyAction)
2343 return;
2345 QList<QUrl> urls = event->mimeData()->urls();
2346 for (int i = 0; i < urls.size(); i++)
2347 if (!urls.at(i).isLocalFile())
2348 return;
2350 event->acceptProposedAction();
2353 void GUI::dropEvent(QDropEvent *event)
2355 MapAction *lastReady = 0;
2356 QList<QUrl> urls(event->mimeData()->urls());
2357 int silent = 0;
2358 int showError = (urls.size() > 1) ? 2 : 1;
2360 for (int i = 0; i < urls.size(); i++) {
2361 QString file(urls.at(i).toLocalFile());
2363 if (!openFile(file, false, silent)) {
2364 MapAction *a;
2365 if (!loadMap(file, a, silent))
2366 openFile(file, true, showError);
2367 else {
2368 if (a)
2369 lastReady = a;
2374 if (lastReady)
2375 lastReady->trigger();
2377 event->acceptProposedAction();
2380 QGeoPositionInfoSource *GUI::positionSource(const Options &options)
2382 QGeoPositionInfoSource *source;
2384 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
2385 source = QGeoPositionInfoSource::createSource(options.plugin, this);
2386 #else // QT 5.14
2387 source = QGeoPositionInfoSource::createSource(options.plugin,
2388 options.pluginParams.value(options.plugin), this);
2389 #endif // QT 5.14
2390 if (source)
2391 source->setPreferredPositioningMethods(
2392 QGeoPositionInfoSource::SatellitePositioningMethods);
2394 return source;
2397 void GUI::writeSettings()
2399 #define WRITE(name, value) \
2400 Settings::name.write(settings, value);
2402 QSettings settings(qApp->applicationName(), qApp->applicationName());
2403 settings.clear();
2405 /* Window */
2406 #ifndef Q_OS_ANDROID
2407 settings.beginGroup(SETTINGS_WINDOW);
2408 if (!_windowStates.isEmpty() && !_windowGeometries.isEmpty()) {
2409 WRITE(windowState, _windowStates.first());
2410 WRITE(windowGeometry, _windowGeometries.first());
2411 } else {
2412 WRITE(windowState, saveState());
2413 WRITE(windowGeometry, saveGeometry());
2415 settings.endGroup();
2416 #endif // Q_OS_ANDROID
2418 /* Settings */
2419 settings.beginGroup(SETTINGS_SETTINGS);
2420 WRITE(timeType, _movingTimeAction->isChecked() ? Moving : Total);
2421 WRITE(units, _imperialUnitsAction->isChecked()
2422 ? Imperial : _nauticalUnitsAction->isChecked()
2423 ? Nautical : Metric);
2424 WRITE(coordinatesFormat, _dmsAction->isChecked()
2425 ? DMS : _degreesMinutesAction->isChecked()
2426 ? DegreesMinutes : DecimalDegrees);
2427 #ifndef Q_OS_ANDROID
2428 WRITE(showToolbars, _showToolbarsAction->isChecked());
2429 #endif // Q_OS_ANDROID
2430 settings.endGroup();
2432 /* File */
2433 #ifndef Q_OS_ANDROID
2434 QList<QAction*> recentActions(_recentFilesActionGroup->actions());
2435 QStringList recent;
2436 for (int i = 0; i < recentActions.size(); i++)
2437 recent.append(recentActions.at(i)->text());
2439 settings.beginGroup(SETTINGS_FILE);
2440 WRITE(recentDataFiles, recent);
2441 settings.endGroup();
2442 #endif // Q_OS_ANDROID
2444 /* Map */
2445 settings.beginGroup(SETTINGS_MAP);
2446 WRITE(activeMap, _map->name());
2447 WRITE(showMap, _showMapAction->isChecked());
2448 WRITE(cursorCoordinates, _showCoordinatesAction->isChecked());
2449 settings.endGroup();
2451 /* Graph */
2452 settings.beginGroup(SETTINGS_GRAPH);
2453 WRITE(showGraphs, _showGraphsAction->isChecked());
2454 WRITE(graphType, _timeGraphAction->isChecked() ? Time : Distance);
2455 WRITE(showGrid, _showGraphGridAction->isChecked());
2456 WRITE(sliderInfo, _showGraphSliderInfoAction->isChecked());
2457 #ifdef Q_OS_ANDROID
2458 WRITE(showGraphTabs, _showGraphTabsAction->isChecked());
2459 #endif // Q_OS_ANDROID
2460 settings.endGroup();
2462 /* POI */
2463 QList<QAction*> disabledActions(_poisActionGroup->actions());
2464 QStringList disabled;
2465 for (int i = 0; i < disabledActions.size(); i++)
2466 if (!disabledActions.at(i)->isChecked())
2467 disabled.append(disabledActions.at(i)->data().toString());
2469 settings.beginGroup(SETTINGS_POI);
2470 WRITE(showPoi, _showPOIAction->isChecked());
2471 WRITE(poiOverlap, _overlapPOIAction->isChecked());
2472 WRITE(poiLabels, _showPOILabelsAction->isChecked());
2473 WRITE(poiIcons, _showPOIIconsAction->isChecked());
2474 WRITE(disabledPoiFiles, disabled);
2475 settings.endGroup();
2477 /* Data */
2478 MarkerInfoItem::Type mi;
2479 if (_showMarkerDateAction->isChecked())
2480 mi = MarkerInfoItem::Date;
2481 else if (_showMarkerCoordinatesAction->isChecked())
2482 mi = MarkerInfoItem::Position;
2483 else
2484 mi = MarkerInfoItem::None;
2486 settings.beginGroup(SETTINGS_DATA);
2487 WRITE(tracks, _showTracksAction->isChecked());
2488 WRITE(routes, _showRoutesAction->isChecked());
2489 WRITE(waypoints, _showWaypointsAction->isChecked());
2490 WRITE(areas, _showAreasAction->isChecked());
2491 WRITE(waypointIcons, _showWaypointIconsAction->isChecked());
2492 WRITE(waypointLabels, _showWaypointLabelsAction->isChecked());
2493 WRITE(routeWaypoints, _showRouteWaypointsAction->isChecked());
2494 WRITE(pathTicks, _showTicksAction->isChecked());
2495 WRITE(positionMarkers, _showMarkersAction->isChecked()
2496 || _showMarkerDateAction->isChecked()
2497 || _showMarkerCoordinatesAction->isChecked());
2498 WRITE(markerInfo, mi);
2499 WRITE(useStyles, _useStylesAction->isChecked());
2500 settings.endGroup();
2502 /* Position */
2503 settings.beginGroup(SETTINGS_POSITION);
2504 WRITE(showPosition, _showPositionAction->isChecked());
2505 WRITE(followPosition, _followPositionAction->isChecked());
2506 WRITE(positionCoordinates, _showPositionCoordinatesAction->isChecked());
2507 WRITE(motionInfo, _showMotionInfoAction->isChecked());
2508 settings.endGroup();
2510 /* PDF export */
2511 settings.beginGroup(SETTINGS_PDF_EXPORT);
2512 WRITE(pdfOrientation, _pdfExport.orientation);
2513 WRITE(pdfResolution, _pdfExport.resolution);
2514 WRITE(pdfSize, _pdfExport.paperSize);
2515 WRITE(pdfMarginLeft, _pdfExport.margins.left());
2516 WRITE(pdfMarginTop, _pdfExport.margins.top());
2517 WRITE(pdfMarginRight, _pdfExport.margins.right());
2518 WRITE(pdfMarginBottom, _pdfExport.margins.bottom());
2519 WRITE(pdfFileName, _pdfExport.fileName);
2520 settings.endGroup();
2522 /* PNG export */
2523 settings.beginGroup(SETTINGS_PNG_EXPORT);
2524 WRITE(pngWidth, _pngExport.size.width());
2525 WRITE(pngHeight, _pngExport.size.height());
2526 WRITE(pngMarginLeft, _pngExport.margins.left());
2527 WRITE(pngMarginTop, _pngExport.margins.top());
2528 WRITE(pngMarginRight, _pngExport.margins.right());
2529 WRITE(pngMarginBottom, _pngExport.margins.bottom());
2530 WRITE(pngAntialiasing, _pngExport.antialiasing);
2531 WRITE(pngFileName, _pngExport.fileName);
2532 settings.endGroup();
2534 /* Options */
2535 settings.beginGroup(SETTINGS_OPTIONS);
2536 WRITE(paletteColor, _options.palette.color());
2537 WRITE(paletteShift, _options.palette.shift());
2538 WRITE(mapOpacity, _options.mapOpacity);
2539 WRITE(backgroundColor, _options.backgroundColor);
2540 WRITE(crosshairColor, _options.crosshairColor);
2541 WRITE(infoColor, _options.infoColor);
2542 WRITE(infoBackground, _options.infoBackground);
2543 WRITE(trackWidth, _options.trackWidth);
2544 WRITE(routeWidth, _options.routeWidth);
2545 WRITE(areaWidth, _options.areaWidth);
2546 WRITE(trackStyle, (int)_options.trackStyle);
2547 WRITE(routeStyle, (int)_options.routeStyle);
2548 WRITE(areaStyle, (int)_options.areaStyle);
2549 WRITE(areaOpacity, _options.areaOpacity);
2550 WRITE(waypointSize, _options.waypointSize);
2551 WRITE(waypointColor, _options.waypointColor);
2552 WRITE(poiSize, _options.poiSize);
2553 WRITE(poiColor, _options.poiColor);
2554 WRITE(graphWidth, _options.graphWidth);
2555 WRITE(pathAntiAliasing, _options.pathAntiAliasing);
2556 WRITE(graphAntiAliasing, _options.graphAntiAliasing);
2557 WRITE(elevationFilter, _options.elevationFilter);
2558 WRITE(speedFilter, _options.speedFilter);
2559 WRITE(heartRateFilter, _options.heartRateFilter);
2560 WRITE(cadenceFilter, _options.cadenceFilter);
2561 WRITE(powerFilter, _options.powerFilter);
2562 WRITE(outlierEliminate, _options.outlierEliminate);
2563 WRITE(automaticPause, _options.automaticPause);
2564 WRITE(pauseSpeed, _options.pauseSpeed);
2565 WRITE(pauseInterval, _options.pauseInterval);
2566 WRITE(useReportedSpeed, _options.useReportedSpeed);
2567 WRITE(dataUseDEM, _options.dataUseDEM);
2568 WRITE(secondaryElevation, _options.showSecondaryElevation);
2569 WRITE(secondarySpeed, _options.showSecondarySpeed);
2570 WRITE(timeZone, QVariant::fromValue(_options.timeZone));
2571 WRITE(useSegments, _options.useSegments);
2572 WRITE(poiRadius, _options.poiRadius);
2573 WRITE(demURL, _options.demURL);
2574 WRITE(demAuthentication, _options.demAuthorization);
2575 WRITE(demUsername, _options.demUsername);
2576 WRITE(demPassword, _options.demPassword);
2577 WRITE(positionPlugin(), _options.plugin);
2578 WRITE(positionPluginParameters, _options.pluginParams);
2579 WRITE(useOpenGL, _options.useOpenGL);
2580 WRITE(enableHTTP2, _options.enableHTTP2);
2581 WRITE(pixmapCache, _options.pixmapCache);
2582 WRITE(demCache, _options.demCache);
2583 WRITE(connectionTimeout, _options.connectionTimeout);
2584 WRITE(hiresPrint, _options.hiresPrint);
2585 WRITE(printName, _options.printName);
2586 WRITE(printDate, _options.printDate);
2587 WRITE(printDistance, _options.printDistance);
2588 WRITE(printTime, _options.printTime);
2589 WRITE(printMovingTime, _options.printMovingTime);
2590 WRITE(printItemCount, _options.printItemCount);
2591 WRITE(separateGraphPage, _options.separateGraphPage);
2592 WRITE(sliderColor, _options.sliderColor);
2593 WRITE(outputProjection, _options.outputProjection);
2594 WRITE(inputProjection, _options.inputProjection);
2595 WRITE(hidpiMap, _options.hidpiMap);
2596 WRITE(dataPath, _options.dataPath);
2597 WRITE(mapsPath, _options.mapsPath);
2598 WRITE(poiPath, _options.poiPath);
2599 settings.endGroup();
2602 void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs,
2603 QStringList &recentFiles)
2605 #define READ(name) \
2606 (Settings::name.read(settings))
2608 QSettings settings(qApp->applicationName(), qApp->applicationName());
2610 #ifndef Q_OS_ANDROID
2611 settings.beginGroup(SETTINGS_WINDOW);
2612 restoreGeometry(READ(windowGeometry).toByteArray());
2613 restoreState(READ(windowState).toByteArray());
2614 settings.endGroup();
2615 #endif // Q_OS_ANDROID
2617 /* Settings */
2618 settings.beginGroup(SETTINGS_SETTINGS);
2619 TimeType tt = (TimeType)READ(timeType).toInt();
2620 if (tt == Moving)
2621 _movingTimeAction->setChecked(true);
2622 else
2623 _totalTimeAction->setChecked(true);
2624 setTimeType(tt);
2626 Units u = (Units)READ(units).toInt();
2627 if (u == Imperial)
2628 _imperialUnitsAction->setChecked(true);
2629 else if (u == Nautical)
2630 _nauticalUnitsAction->setChecked(true);
2631 else
2632 _metricUnitsAction->setChecked(true);
2633 setUnits(u);
2635 CoordinatesFormat cf = (CoordinatesFormat)READ(coordinatesFormat).toInt();
2636 if (cf == DMS)
2637 _dmsAction->setChecked(true);
2638 else if (cf == DegreesMinutes)
2639 _degreesMinutesAction->setChecked(true);
2640 else
2641 _decimalDegreesAction->setChecked(true);
2642 setCoordinatesFormat(cf);
2644 #ifndef Q_OS_ANDROID
2645 if (READ(showToolbars).toBool())
2646 _showToolbarsAction->setChecked(true);
2647 else
2648 showToolbars(false);
2649 #endif // Q_OS_ANDROID
2650 settings.endGroup();
2652 /* File */
2653 #ifndef Q_OS_ANDROID
2654 settings.beginGroup(SETTINGS_FILE);
2655 recentFiles = READ(recentDataFiles);
2656 settings.endGroup();
2657 #else // Q_OS_ANDROID
2658 Q_UNUSED(recentFiles);
2659 #endif // Q_OS_ANDROID
2661 /* Map */
2662 settings.beginGroup(SETTINGS_MAP);
2663 if (READ(showMap).toBool()) {
2664 _showMapAction->setChecked(true);
2665 _mapView->showMap(true);
2667 if (READ(cursorCoordinates).toBool()) {
2668 _showCoordinatesAction->setChecked(true);
2669 _mapView->showCursorCoordinates(true);
2671 activeMap = READ(activeMap).toString();
2672 settings.endGroup();
2674 /* Graph */
2675 settings.beginGroup(SETTINGS_GRAPH);
2676 if (READ(showGraphs).toBool())
2677 _showGraphsAction->setChecked(true);
2678 else
2679 showGraphs(false);
2681 GraphType gt = (GraphType)READ(graphType).toInt();
2682 if (gt == Time)
2683 _timeGraphAction->setChecked(true);
2684 else
2685 _distanceGraphAction->setChecked(true);
2686 setGraphType(gt);
2688 if (READ(showGrid).toBool())
2689 _showGraphGridAction->setChecked(true);
2690 else
2691 showGraphGrids(false);
2693 if (READ(sliderInfo).toBool())
2694 _showGraphSliderInfoAction->setChecked(true);
2695 else
2696 showGraphSliderInfo(false);
2698 #ifdef Q_OS_ANDROID
2699 if (READ(showGraphTabs).toBool())
2700 _showGraphTabsAction->setChecked(true);
2701 else
2702 showGraphTabs(false);
2703 #endif // Q_OS_ANDROID
2704 settings.endGroup();
2706 /* POI */
2707 settings.beginGroup(SETTINGS_POI);
2708 if (READ(poiOverlap).toBool()) {
2709 _overlapPOIAction->setChecked(true);
2710 _mapView->showOverlappedPOIs(true);
2712 if (READ(poiIcons).toBool()) {
2713 _showPOIIconsAction->setChecked(true);
2714 _mapView->showPOIIcons(true);
2716 if (READ(poiLabels).toBool()) {
2717 _showPOILabelsAction->setChecked(true);
2718 _mapView->showPOILabels(true);
2720 if (READ(showPoi).toBool()) {
2721 _showPOIAction->setChecked(true);
2722 _mapView->showPOI(true);
2724 disabledPOIs = READ(disabledPoiFiles);
2725 settings.endGroup();
2727 /* Data */
2728 settings.beginGroup(SETTINGS_DATA);
2729 if (READ(tracks).toBool()) {
2730 _showTracksAction->setChecked(true);
2731 _mapView->showTracks(true);
2732 for (int i = 0; i < _tabs.count(); i++)
2733 _tabs.at(i)->showTracks(true);
2735 if (READ(routes).toBool()) {
2736 _showRoutesAction->setChecked(true);
2737 _mapView->showRoutes(true);
2738 for (int i = 0; i < _tabs.count(); i++)
2739 _tabs.at(i)->showRoutes(true);
2741 if (READ(waypoints).toBool()) {
2742 _showWaypointsAction->setChecked(true);
2743 _mapView->showWaypoints(true);
2745 if (READ(areas).toBool()) {
2746 _showAreasAction->setChecked(true);
2747 _mapView->showAreas(true);
2749 if (READ(waypointIcons).toBool()) {
2750 _showWaypointIconsAction->setChecked(true);
2751 _mapView->showWaypointIcons(true);
2753 if (READ(waypointLabels).toBool()) {
2754 _showWaypointLabelsAction->setChecked(true);
2755 _mapView->showWaypointLabels(true);
2757 if (READ(routeWaypoints).toBool()) {
2758 _showRouteWaypointsAction->setChecked(true);
2759 _mapView->showRouteWaypoints(true);
2761 if (READ(pathTicks).toBool()) {
2762 _showTicksAction->setChecked(true);
2763 _mapView->showTicks(true);
2765 if (READ(useStyles).toBool()) {
2766 _useStylesAction->setChecked(true);
2767 _mapView->useStyles(true);
2769 if (READ(positionMarkers).toBool()) {
2770 MarkerInfoItem::Type mt = (MarkerInfoItem::Type)READ(markerInfo).toInt();
2771 if (mt == MarkerInfoItem::Position)
2772 _showMarkerCoordinatesAction->setChecked(true);
2773 else if (mt == MarkerInfoItem::Date)
2774 _showMarkerDateAction->setChecked(true);
2775 else
2776 _showMarkersAction->setChecked(true);
2778 _mapView->showMarkers(true);
2779 _mapView->showMarkerInfo(mt);
2780 } else
2781 _hideMarkersAction->setChecked(true);
2782 settings.endGroup();
2784 /* Position */
2785 settings.beginGroup(SETTINGS_POSITION);
2786 if (READ(showPosition).toBool()) {
2787 _showPositionAction->setChecked(true);
2788 _mapView->showPosition(true);
2790 if (READ(followPosition).toBool()) {
2791 _followPositionAction->setChecked(true);
2792 _mapView->followPosition(true);
2794 if (READ(positionCoordinates).toBool()) {
2795 _showPositionCoordinatesAction->setChecked(true);
2796 _mapView->showPositionCoordinates(true);
2798 if (READ(motionInfo).toBool()) {
2799 _showMotionInfoAction->setChecked(true);
2800 _mapView->showMotionInfo(true);
2802 settings.endGroup();
2804 /* PDF export */
2805 settings.beginGroup(SETTINGS_PDF_EXPORT);
2806 _pdfExport.orientation = (QPageLayout::Orientation)READ(pdfOrientation)
2807 .toInt();
2808 _pdfExport.resolution = READ(pdfResolution).toInt();
2809 _pdfExport.paperSize = (QPageSize::PageSizeId)READ(pdfSize).toInt();
2810 _pdfExport.margins = QMarginsF(READ(pdfMarginLeft).toReal(),
2811 READ(pdfMarginTop).toReal(), READ(pdfMarginRight).toReal(),
2812 READ(pdfMarginBottom).toReal());
2813 _pdfExport.fileName = READ(pdfFileName).toString();
2814 settings.endGroup();
2816 /* PNG export */
2817 settings.beginGroup(SETTINGS_PNG_EXPORT);
2818 _pngExport.size = QSize(READ(pngWidth).toInt(), READ(pngHeight).toInt());
2819 _pngExport.margins = QMargins(READ(pngMarginLeft).toInt(),
2820 READ(pngMarginTop).toInt(), READ(pngMarginRight).toInt(),
2821 READ(pngMarginBottom).toInt());
2822 _pngExport.antialiasing = READ(pngAntialiasing).toBool();
2823 _pngExport.fileName = READ(pngFileName).toString();
2824 settings.endGroup();
2826 /* Options */
2827 settings.beginGroup(SETTINGS_OPTIONS);
2828 _options.palette = Palette(READ(paletteColor).value<QColor>(),
2829 READ(paletteShift).toDouble());
2830 _options.mapOpacity = READ(mapOpacity).toInt();
2831 _options.backgroundColor = READ(backgroundColor).value<QColor>();
2832 _options.crosshairColor = READ(crosshairColor).value<QColor>();
2833 _options.infoColor = READ(infoColor).value<QColor>();
2834 _options.infoBackground = READ(infoBackground).toBool();
2835 _options.trackWidth = READ(trackWidth).toInt();
2836 _options.routeWidth = READ(routeWidth).toInt();
2837 _options.areaWidth = READ(areaWidth).toInt();
2838 _options.trackStyle = (Qt::PenStyle)READ(trackStyle).toInt();
2839 _options.routeStyle = (Qt::PenStyle)READ(routeStyle).toInt();
2840 _options.areaStyle = (Qt::PenStyle)READ(areaStyle).toInt();
2841 _options.areaOpacity = READ(areaOpacity).toInt();
2842 _options.pathAntiAliasing = READ(pathAntiAliasing).toBool();
2843 _options.waypointSize = READ(waypointSize).toInt();
2844 _options.waypointColor = READ(waypointColor).value<QColor>();
2845 _options.poiSize = READ(poiSize).toInt();
2846 _options.poiColor = READ(poiColor).value<QColor>();
2847 _options.graphWidth = READ(graphWidth).toInt();
2848 _options.graphAntiAliasing = READ(graphAntiAliasing).toBool();
2849 _options.elevationFilter = READ(elevationFilter).toInt();
2850 _options.speedFilter = READ(speedFilter).toInt();
2851 _options.heartRateFilter = READ(heartRateFilter).toInt();
2852 _options.cadenceFilter = READ(cadenceFilter).toInt();
2853 _options.powerFilter = READ(powerFilter).toInt();
2854 _options.outlierEliminate = READ(outlierEliminate).toBool();
2855 _options.pauseSpeed = READ(pauseSpeed).toFloat();
2856 _options.automaticPause = READ(automaticPause).toBool();
2857 _options.pauseInterval = READ(pauseInterval).toInt();
2858 _options.useReportedSpeed = READ(useReportedSpeed).toBool();
2859 _options.dataUseDEM = READ(dataUseDEM).toBool();
2860 _options.showSecondaryElevation = READ(secondaryElevation).toBool();
2861 _options.showSecondarySpeed = READ(secondarySpeed).toBool();
2862 _options.timeZone = READ(timeZone).value<TimeZoneInfo>();
2863 _options.useSegments = READ(useSegments).toBool();
2864 _options.poiRadius = READ(poiRadius).toInt();
2865 _options.demURL = READ(demURL).toString();
2866 _options.demAuthorization = READ(demAuthentication).toBool();
2867 _options.demUsername = READ(demUsername).toString();
2868 _options.demPassword = READ(demPassword).toString();
2869 _options.plugin = READ(positionPlugin()).toString();
2870 _options.pluginParams = READ(positionPluginParameters);
2871 _options.useOpenGL = READ(useOpenGL).toBool();
2872 _options.enableHTTP2 = READ(enableHTTP2).toBool();
2873 _options.pixmapCache = READ(pixmapCache).toInt();
2874 _options.demCache = READ(demCache).toInt();
2875 _options.connectionTimeout = READ(connectionTimeout).toInt();
2876 _options.hiresPrint = READ(hiresPrint).toBool();
2877 _options.printName = READ(printName).toBool();
2878 _options.printDate = READ(printDate).toBool();
2879 _options.printDistance = READ(printDistance).toBool();
2880 _options.printTime = READ(printTime).toBool();
2881 _options.printMovingTime = READ(printMovingTime).toBool();
2882 _options.printItemCount = READ(printItemCount).toBool();
2883 _options.separateGraphPage = READ(separateGraphPage).toBool();
2884 _options.sliderColor = READ(sliderColor).value<QColor>();
2885 _options.outputProjection = READ(outputProjection).toInt();
2886 _options.inputProjection = READ(inputProjection).toInt();
2887 _options.hidpiMap = READ(hidpiMap).toBool();
2888 _options.dataPath = READ(dataPath).toString();
2889 _options.mapsPath = READ(mapsPath).toString();
2890 _options.poiPath = READ(poiPath).toString();
2891 settings.endGroup();
2893 loadOptions();
2896 void GUI::loadOptions()
2898 _positionSource = positionSource(_options);
2899 _showPositionAction->setEnabled(_positionSource != 0);
2901 _mapView->setPalette(_options.palette);
2902 _mapView->setMapOpacity(_options.mapOpacity);
2903 _mapView->setBackgroundColor(_options.backgroundColor);
2904 _mapView->setCrosshairColor(_options.crosshairColor);
2905 _mapView->setInfoColor(_options.infoColor);
2906 _mapView->drawInfoBackground(_options.infoBackground);
2907 _mapView->setTrackWidth(_options.trackWidth);
2908 _mapView->setRouteWidth(_options.routeWidth);
2909 _mapView->setAreaWidth(_options.areaWidth);
2910 _mapView->setTrackStyle(_options.trackStyle);
2911 _mapView->setRouteStyle(_options.routeStyle);
2912 _mapView->setAreaStyle(_options.areaStyle);
2913 _mapView->setAreaOpacity(_options.areaOpacity);
2914 _mapView->setWaypointSize(_options.waypointSize);
2915 _mapView->setWaypointColor(_options.waypointColor);
2916 _mapView->setPOISize(_options.poiSize);
2917 _mapView->setPOIColor(_options.poiColor);
2918 _mapView->setRenderHint(QPainter::Antialiasing, _options.pathAntiAliasing);
2919 _mapView->setMarkerColor(_options.sliderColor);
2920 _mapView->useOpenGL(_options.useOpenGL);
2921 _mapView->setMapConfig(CRS::projection(_options.inputProjection),
2922 CRS::projection(4326, _options.outputProjection), _options.hidpiMap);
2923 _mapView->setTimeZone(_options.timeZone.zone());
2924 _mapView->setPositionSource(_positionSource);
2926 for (int i = 0; i < _tabs.count(); i++) {
2927 _tabs.at(i)->setPalette(_options.palette);
2928 _tabs.at(i)->setGraphWidth(_options.graphWidth);
2929 _tabs.at(i)->setRenderHint(QPainter::Antialiasing,
2930 _options.graphAntiAliasing);
2931 _tabs.at(i)->setSliderColor(_options.sliderColor);
2932 if (_options.useOpenGL)
2933 _tabs.at(i)->useOpenGL(true);
2936 Track::setElevationFilter(_options.elevationFilter);
2937 Track::setSpeedFilter(_options.speedFilter);
2938 Track::setHeartRateFilter(_options.heartRateFilter);
2939 Track::setCadenceFilter(_options.cadenceFilter);
2940 Track::setPowerFilter(_options.powerFilter);
2941 Track::setOutlierElimination(_options.outlierEliminate);
2942 Track::setAutomaticPause(_options.automaticPause);
2943 Track::setPauseSpeed(_options.pauseSpeed);
2944 Track::setPauseInterval(_options.pauseInterval);
2945 Track::useReportedSpeed(_options.useReportedSpeed);
2946 Track::useDEM(_options.dataUseDEM);
2947 Track::showSecondaryElevation(_options.showSecondaryElevation);
2948 Track::showSecondarySpeed(_options.showSecondarySpeed);
2949 Track::useSegments(_options.useSegments);
2950 Route::useDEM(_options.dataUseDEM);
2951 Route::showSecondaryElevation(_options.showSecondaryElevation);
2952 Waypoint::useDEM(_options.dataUseDEM);
2953 Waypoint::showSecondaryElevation(_options.showSecondaryElevation);
2955 Downloader::enableHTTP2(_options.enableHTTP2);
2956 Downloader::setTimeout(_options.connectionTimeout);
2958 QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
2959 DEM::setCacheSize(_options.demCache * 1024);
2961 _poi->setRadius(_options.poiRadius);
2963 _dem->setUrl(_options.demURL);
2964 if (_options.demAuthorization)
2965 _dem->setAuthorization(Authorization(_options.demUsername,
2966 _options.demPassword));
2968 _dataDir = _options.dataPath;
2969 _mapDir = _options.mapsPath;
2970 _poiDir = _options.poiPath;
2973 void GUI::updateOptions(const Options &options)
2975 #define SET_VIEW_OPTION(option, action) \
2976 if (options.option != _options.option) \
2977 _mapView->action(options.option)
2978 #define SET_TAB_OPTION(option, action) \
2979 if (options.option != _options.option) \
2980 for (int i = 0; i < _tabs.count(); i++) \
2981 _tabs.at(i)->action(options.option)
2982 #define SET_TRACK_OPTION(option, action) \
2983 if (options.option != _options.option) { \
2984 Track::action(options.option); \
2985 reload = true; \
2987 #define SET_ROUTE_OPTION(option, action) \
2988 if (options.option != _options.option) { \
2989 Route::action(options.option); \
2990 reload = true; \
2992 #define SET_WAYPOINT_OPTION(option, action) \
2993 if (options.option != _options.option) { \
2994 Waypoint::action(options.option); \
2995 reload = true; \
2998 bool reload = false;
3000 SET_VIEW_OPTION(palette, setPalette);
3001 SET_VIEW_OPTION(mapOpacity, setMapOpacity);
3002 SET_VIEW_OPTION(backgroundColor, setBackgroundColor);
3003 SET_VIEW_OPTION(trackWidth, setTrackWidth);
3004 SET_VIEW_OPTION(routeWidth, setRouteWidth);
3005 SET_VIEW_OPTION(areaWidth, setAreaWidth);
3006 SET_VIEW_OPTION(trackStyle, setTrackStyle);
3007 SET_VIEW_OPTION(routeStyle, setRouteStyle);
3008 SET_VIEW_OPTION(areaStyle, setAreaStyle);
3009 SET_VIEW_OPTION(areaOpacity, setAreaOpacity);
3010 SET_VIEW_OPTION(waypointSize, setWaypointSize);
3011 SET_VIEW_OPTION(waypointColor, setWaypointColor);
3012 SET_VIEW_OPTION(poiSize, setPOISize);
3013 SET_VIEW_OPTION(poiColor, setPOIColor);
3014 SET_VIEW_OPTION(pathAntiAliasing, useAntiAliasing);
3015 SET_VIEW_OPTION(useOpenGL, useOpenGL);
3016 SET_VIEW_OPTION(sliderColor, setMarkerColor);
3017 SET_VIEW_OPTION(crosshairColor, setCrosshairColor);
3018 SET_VIEW_OPTION(infoColor, setInfoColor);
3019 SET_VIEW_OPTION(infoBackground, drawInfoBackground);
3021 if (options.plugin != _options.plugin
3022 || options.pluginParams.value(options.plugin)
3023 != _options.pluginParams.value(_options.plugin)) {
3024 QGeoPositionInfoSource *source = positionSource(options);
3025 _showPositionAction->setEnabled(source != 0);
3026 _mapView->setPositionSource(source);
3027 delete _positionSource;
3028 _positionSource = source;
3031 if (options.hidpiMap != _options.hidpiMap
3032 || options.outputProjection != _options.outputProjection
3033 || options.inputProjection != _options.inputProjection)
3034 _mapView->setMapConfig(CRS::projection(options.inputProjection),
3035 CRS::projection(4326, options.outputProjection), options.hidpiMap);
3037 if (options.timeZone != _options.timeZone) {
3038 _mapView->setTimeZone(options.timeZone.zone());
3039 _dateRange.first = _dateRange.first.toTimeZone(options.timeZone.zone());
3040 _dateRange.second = _dateRange.second.toTimeZone(options.timeZone.zone());
3043 SET_TAB_OPTION(palette, setPalette);
3044 SET_TAB_OPTION(graphWidth, setGraphWidth);
3045 SET_TAB_OPTION(graphAntiAliasing, useAntiAliasing);
3046 SET_TAB_OPTION(useOpenGL, useOpenGL);
3047 SET_TAB_OPTION(sliderColor, setSliderColor);
3049 SET_TRACK_OPTION(elevationFilter, setElevationFilter);
3050 SET_TRACK_OPTION(speedFilter, setSpeedFilter);
3051 SET_TRACK_OPTION(heartRateFilter, setHeartRateFilter);
3052 SET_TRACK_OPTION(cadenceFilter, setCadenceFilter);
3053 SET_TRACK_OPTION(powerFilter, setPowerFilter);
3054 SET_TRACK_OPTION(outlierEliminate, setOutlierElimination);
3055 SET_TRACK_OPTION(automaticPause, setAutomaticPause);
3056 SET_TRACK_OPTION(pauseSpeed, setPauseSpeed);
3057 SET_TRACK_OPTION(pauseInterval, setPauseInterval);
3058 SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed);
3059 SET_TRACK_OPTION(dataUseDEM, useDEM);
3060 SET_TRACK_OPTION(showSecondaryElevation, showSecondaryElevation);
3061 SET_TRACK_OPTION(showSecondarySpeed, showSecondarySpeed);
3062 SET_TRACK_OPTION(useSegments, useSegments);
3064 SET_ROUTE_OPTION(dataUseDEM, useDEM);
3065 SET_ROUTE_OPTION(showSecondaryElevation, showSecondaryElevation);
3067 SET_WAYPOINT_OPTION(dataUseDEM, useDEM);
3068 SET_WAYPOINT_OPTION(showSecondaryElevation, showSecondaryElevation);
3070 if (options.poiRadius != _options.poiRadius)
3071 _poi->setRadius(options.poiRadius);
3073 if (options.demURL != _options.demURL)
3074 _dem->setUrl(options.demURL);
3075 if (options.demAuthorization != _options.demAuthorization
3076 || options.demUsername != _options.demUsername
3077 || options.demPassword != _options.demPassword)
3078 _dem->setAuthorization(options.demAuthorization
3079 ? Authorization(options.demUsername, options.demPassword)
3080 : Authorization());
3082 if (options.pixmapCache != _options.pixmapCache)
3083 QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
3084 if (options.demCache != _options.demCache)
3085 DEM::setCacheSize(options.demCache * 1024);
3087 if (options.connectionTimeout != _options.connectionTimeout)
3088 Downloader::setTimeout(options.connectionTimeout);
3089 if (options.enableHTTP2 != _options.enableHTTP2)
3090 Downloader::enableHTTP2(options.enableHTTP2);
3092 if (options.dataPath != _options.dataPath)
3093 _dataDir = options.dataPath;
3094 if (options.mapsPath != _options.mapsPath)
3095 _mapDir = options.mapsPath;
3096 if (options.poiPath != _options.poiPath)
3097 _poiDir = options.poiPath;
3099 if (reload)
3100 reloadFiles();
3102 _options = options;
3104 updateDEMDownloadAction();
3107 void GUI::loadInitialMaps(const QString &selected)
3109 // Load the maps
3110 QString mapDir(ProgramPaths::mapDir());
3111 if (mapDir.isNull())
3112 return;
3114 TreeNode<Map*> maps(MapList::loadMaps(mapDir, _mapView->inputProjection()));
3115 createMapNodeMenu(createMapActionsNode(maps), _mapMenu, _mapsEnd);
3117 // Select the active map according to the user settings
3118 QAction *ma = mapAction(selected);
3119 if (ma) {
3120 ma->trigger();
3121 _showMapAction->setEnabled(true);
3122 _clearMapCacheAction->setEnabled(true);
3126 void GUI::loadInitialPOIs(const QStringList &disabled)
3128 // Load the POI files
3129 QString poiDir(ProgramPaths::poiDir());
3130 if (poiDir.isNull())
3131 return;
3133 TreeNode<QString> poiFiles(_poi->loadDir(poiDir));
3134 createPOINodeMenu(createPOIActionsNode(poiFiles), _poiMenu, _poisEnd);
3136 // Enable/disable the files according to the user settings
3137 QList<QAction*> poiActions(_poisActionGroup->actions());
3138 for (int i = 0; i < poiActions.count(); i++)
3139 poiActions.at(i)->setChecked(true);
3140 for (int i = 0; i < disabled.size(); i++) {
3141 const QString &file = disabled.at(i);
3142 if (_poi->enableFile(file, false)) {
3143 for (int j = 0; j < poiActions.size(); j++)
3144 if (poiActions.at(j)->data().toString() == file)
3145 poiActions.at(j)->setChecked(false);
3149 _selectAllPOIAction->setEnabled(!poiActions.isEmpty());
3150 _unselectAllPOIAction->setEnabled(!poiActions.isEmpty());
3153 #ifndef Q_OS_ANDROID
3154 void GUI::loadRecentFiles(const QStringList &files)
3156 QAction *before = _recentFilesEnd;
3158 for (int i = 0; i < files.size(); i++) {
3159 QAction *a = new QAction(files.at(i), _recentFilesActionGroup);
3160 _recentFilesMenu->insertAction(before, a);
3161 before = a;
3164 if (!files.isEmpty())
3165 _recentFilesMenu->setEnabled(true);
3167 #endif // Q_OS_ANDROID
3169 QAction *GUI::mapAction(const QString &name)
3171 QList<QAction *> maps(_mapsActionGroup->actions());
3173 // Last map
3174 for (int i = 0; i < maps.count(); i++) {
3175 Map *map = maps.at(i)->data().value<Map*>();
3176 if (map->name() == name && map->isReady())
3177 return maps.at(i);
3180 // Any usable map
3181 for (int i = 0; i < maps.count(); i++) {
3182 Map *map = maps.at(i)->data().value<Map*>();
3183 if (map->isReady())
3184 return maps.at(i);
3187 return 0;
3190 Units GUI::units() const
3192 return _imperialUnitsAction->isChecked() ? Imperial
3193 : _nauticalUnitsAction->isChecked() ? Nautical : Metric;
3196 qreal GUI::distance() const
3198 qreal dist = 0;
3200 if (_showTracksAction->isChecked())
3201 dist += _trackDistance;
3202 if (_showRoutesAction->isChecked())
3203 dist += _routeDistance;
3205 return dist;
3208 qreal GUI::time() const
3210 return (_showTracksAction->isChecked()) ? _time : 0;
3213 qreal GUI::movingTime() const
3215 return (_showTracksAction->isChecked()) ? _movingTime : 0;
3218 void GUI::show()
3220 QMainWindow::show();
3222 QWindow *w = windowHandle();
3223 connect(w->screen(), &QScreen::logicalDotsPerInchChanged, this,
3224 &GUI::logicalDotsPerInchChanged);
3225 connect(w, &QWindow::screenChanged, this, &GUI::screenChanged);
3227 _mapView->fitContentToSize();
3230 void GUI::screenChanged(QScreen *screen)
3232 _mapView->setDevicePixelRatio(devicePixelRatioF());
3234 disconnect(SIGNAL(logicalDotsPerInchChanged(qreal)), this,
3235 SLOT(logicalDotsPerInchChanged(qreal)));
3236 connect(screen, &QScreen::logicalDotsPerInchChanged, this,
3237 &GUI::logicalDotsPerInchChanged);
3240 void GUI::logicalDotsPerInchChanged(qreal dpi)
3242 Q_UNUSED(dpi)
3244 _mapView->setDevicePixelRatio(devicePixelRatioF());