Header includes cleanup
[GPXSee.git] / src / GUI / gui.cpp
blob00367a5550fdea02c51b6adeb7200a0ce4334ba9
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 "navigationwidget.h"
58 #include "gui.h"
61 #define MAX_RECENT_FILES 10
62 #define TOOLBAR_ICON_SIZE 22
64 GUI::GUI()
66 QString activeMap;
67 QStringList disabledPOIs, recentFiles;
69 _poi = new POI(this);
70 _dem = new DEMLoader(ProgramPaths::demDir(true), this);
71 connect(_dem, &DEMLoader::finished, this, &GUI::demLoaded);
73 createMapView();
74 createGraphTabs();
75 createStatusBar();
76 createActions();
77 createMenus();
78 #ifdef Q_OS_ANDROID
79 createNavigation();
80 #else // Q_OS_ANDROID
81 createToolBars();
82 #endif // Q_OS_ANDROID
83 createBrowser();
85 _splitter = new QSplitter();
86 _splitter->setOrientation(Qt::Vertical);
87 _splitter->setChildrenCollapsible(false);
88 _splitter->addWidget(_mapView);
89 _splitter->addWidget(_graphTabWidget);
90 _splitter->setContentsMargins(0, 0, 0, 0);
91 _splitter->setStretchFactor(0, 255);
92 _splitter->setStretchFactor(1, 1);
94 setCentralWidget(_splitter);
96 setWindowIcon(QIcon(APP_ICON));
97 setWindowTitle(APP_NAME);
98 setUnifiedTitleAndToolBarOnMac(true);
99 setAcceptDrops(true);
101 _trackCount = 0;
102 _routeCount = 0;
103 _waypointCount = 0;
104 _areaCount = 0;
105 _trackDistance = 0;
106 _routeDistance = 0;
107 _time = 0;
108 _movingTime = 0;
109 _lastTab = 0;
111 readSettings(activeMap, disabledPOIs, recentFiles);
113 loadInitialMaps(activeMap);
114 loadInitialPOIs(disabledPOIs);
115 #ifndef Q_OS_ANDROID
116 loadRecentFiles(recentFiles);
117 #endif // Q_OS_ANDROID
119 updateGraphTabs();
120 updateStatusBarInfo();
123 void GUI::createBrowser()
125 _browser = new FileBrowser(this);
126 _browser->setFilter(Data::filter());
127 connect(_browser, &FileBrowser::listChanged, this,
128 &GUI::updateNavigationActions);
131 TreeNode<MapAction*> GUI::createMapActionsNode(const TreeNode<Map*> &node)
133 TreeNode<MapAction*> tree(node.name());
135 for (int i = 0; i < node.childs().size(); i++)
136 tree.addChild(createMapActionsNode(node.childs().at(i)));
138 for (int i = 0; i < node.items().size(); i++) {
139 Map *map = node.items().at(i);
140 if (map->isValid()) {
141 MapAction *a = new MapAction(map, _mapsActionGroup);
142 connect(a, &MapAction::loaded, this, &GUI::mapInitialized);
143 tree.addItem(a);
144 } else
145 delete map;
148 return tree;
151 void GUI::mapInitialized()
153 MapAction *action = static_cast<MapAction*>(QObject::sender());
154 Map *map = action->data().value<Map*>();
156 if (map->isValid()) {
157 if (!_mapsActionGroup->checkedAction())
158 action->trigger();
159 _showMapAction->setEnabled(true);
160 _clearMapCacheAction->setEnabled(true);
161 } else {
162 qWarning("%s: %s", qPrintable(map->path()), qPrintable(map->errorString()));
163 action->deleteLater();
167 TreeNode<POIAction *> GUI::createPOIActionsNode(const TreeNode<QString> &node)
169 TreeNode<POIAction*> tree(node.name());
171 for (int i = 0; i < node.childs().size(); i++)
172 tree.addChild(createPOIActionsNode(node.childs().at(i)));
173 for (int i = 0; i < node.items().size(); i++)
174 tree.addItem(new POIAction(node.items().at(i), _poisActionGroup));
176 return tree;
179 void GUI::createActions()
181 QActionGroup *ag;
183 // Action Groups
184 _fileActionGroup = new QActionGroup(this);
185 _fileActionGroup->setExclusive(false);
186 _fileActionGroup->setEnabled(false);
188 _navigationActionGroup = new QActionGroup(this);
189 _navigationActionGroup->setEnabled(false);
191 // General actions
192 #if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
193 _exitAction = new QAction(QIcon::fromTheme(QUIT_NAME, QIcon(QUIT_ICON)),
194 tr("Quit"), this);
195 _exitAction->setShortcut(QUIT_SHORTCUT);
196 _exitAction->setMenuRole(QAction::QuitRole);
197 connect(_exitAction, &QAction::triggered, this, &GUI::close);
198 addAction(_exitAction);
199 #endif // Q_OS_MAC + Q_OS_ANDROID
201 // Help & About
202 _pathsAction = new QAction(tr("Paths"), this);
203 _pathsAction->setMenuRole(QAction::NoRole);
204 connect(_pathsAction, &QAction::triggered, this, &GUI::paths);
205 #ifndef Q_OS_ANDROID
206 _keysAction = new QAction(tr("Keyboard controls"), this);
207 _keysAction->setMenuRole(QAction::NoRole);
208 connect(_keysAction, &QAction::triggered, this, &GUI::keys);
209 #endif // Q_OS_ANDROID
210 _aboutAction = new QAction(QIcon(APP_ICON), tr("About GPXSee"), this);
211 _aboutAction->setMenuRole(QAction::AboutRole);
212 connect(_aboutAction, &QAction::triggered, this, &GUI::about);
214 // File actions
215 _openFileAction = new QAction(QIcon::fromTheme(OPEN_FILE_NAME,
216 QIcon(OPEN_FILE_ICON)), tr("Open..."), this);
217 _openFileAction->setMenuRole(QAction::NoRole);
218 _openFileAction->setShortcut(OPEN_SHORTCUT);
219 connect(_openFileAction, &QAction::triggered, this,
220 QOverload<>::of(&GUI::openFile));
221 addAction(_openFileAction);
222 _openDirAction = new QAction(QIcon::fromTheme(OPEN_DIR_NAME,
223 QIcon(OPEN_DIR_ICON)), tr("Open directory..."), this);
224 _openDirAction->setMenuRole(QAction::NoRole);
225 connect(_openDirAction, &QAction::triggered, this,
226 QOverload<>::of(&GUI::openDir));
227 _printFileAction = new QAction(QIcon::fromTheme(PRINT_FILE_NAME,
228 QIcon(PRINT_FILE_ICON)), tr("Print..."), this);
229 _printFileAction->setMenuRole(QAction::NoRole);
230 _printFileAction->setActionGroup(_fileActionGroup);
231 connect(_printFileAction, &QAction::triggered, this, &GUI::printFile);
232 addAction(_printFileAction);
233 _exportPDFFileAction = new QAction(QIcon::fromTheme(EXPORT_FILE_NAME,
234 QIcon(EXPORT_FILE_ICON)), tr("Export to PDF..."), this);
235 _exportPDFFileAction->setMenuRole(QAction::NoRole);
236 _exportPDFFileAction->setShortcut(PDF_EXPORT_SHORTCUT);
237 _exportPDFFileAction->setActionGroup(_fileActionGroup);
238 connect(_exportPDFFileAction, &QAction::triggered, this, &GUI::exportPDFFile);
239 addAction(_exportPDFFileAction);
240 _exportPNGFileAction = new QAction(QIcon::fromTheme(EXPORT_FILE_NAME,
241 QIcon(EXPORT_FILE_ICON)), tr("Export to PNG..."), this);
242 _exportPNGFileAction->setMenuRole(QAction::NoRole);
243 _exportPNGFileAction->setShortcut(PNG_EXPORT_SHORTCUT);
244 _exportPNGFileAction->setActionGroup(_fileActionGroup);
245 connect(_exportPNGFileAction, &QAction::triggered, this, &GUI::exportPNGFile);
246 addAction(_exportPNGFileAction);
247 _closeFileAction = new QAction(QIcon::fromTheme(CLOSE_FILE_NAME,
248 QIcon(CLOSE_FILE_ICON)), tr("Close"), this);
249 _closeFileAction->setMenuRole(QAction::NoRole);
250 _closeFileAction->setShortcut(CLOSE_SHORTCUT);
251 _closeFileAction->setActionGroup(_fileActionGroup);
252 connect(_closeFileAction, &QAction::triggered, this, &GUI::closeAll);
253 addAction(_closeFileAction);
254 _reloadFileAction = new QAction(QIcon::fromTheme(RELOAD_FILE_NAME,
255 QIcon(RELOAD_FILE_ICON)), tr("Reload"), this);
256 _reloadFileAction->setMenuRole(QAction::NoRole);
257 _reloadFileAction->setShortcut(RELOAD_SHORTCUT);
258 _reloadFileAction->setActionGroup(_fileActionGroup);
259 connect(_reloadFileAction, &QAction::triggered, this, &GUI::reloadFiles);
260 addAction(_reloadFileAction);
261 _statisticsAction = new QAction(tr("Statistics..."), this);
262 _statisticsAction->setMenuRole(QAction::NoRole);
263 _statisticsAction->setShortcut(STATISTICS_SHORTCUT);
264 _statisticsAction->setActionGroup(_fileActionGroup);
265 connect(_statisticsAction, &QAction::triggered, this, &GUI::statistics);
266 addAction(_statisticsAction);
267 #ifndef Q_OS_ANDROID
268 _recentFilesActionGroup = new QActionGroup(this);
269 connect(_recentFilesActionGroup, &QActionGroup::triggered, this,
270 &GUI::recentFileSelected);
271 _clearRecentFilesAction = new QAction(tr("Clear list"), this);
272 _clearRecentFilesAction->setMenuRole(QAction::NoRole);
273 connect(_clearRecentFilesAction, &QAction::triggered, this,
274 &GUI::clearRecentFiles);
275 #endif // Q_OS_ANDROID
277 // POI actions
278 _poisActionGroup = new QActionGroup(this);
279 _poisActionGroup->setExclusive(false);
280 connect(_poisActionGroup, &QActionGroup::triggered, this,
281 &GUI::poiFileChecked);
282 _openPOIAction = new QAction(QIcon::fromTheme(OPEN_FILE_NAME,
283 QIcon(OPEN_FILE_ICON)), tr("Load POI file..."), this);
284 _openPOIAction->setMenuRole(QAction::NoRole);
285 connect(_openPOIAction, &QAction::triggered, this,
286 QOverload<>::of(&GUI::openPOIFile));
287 _selectAllPOIAction = new QAction(tr("Select all files"), this);
288 _selectAllPOIAction->setMenuRole(QAction::NoRole);
289 _selectAllPOIAction->setEnabled(false);
290 connect(_selectAllPOIAction, &QAction::triggered, this,
291 &GUI::selectAllPOIs);
292 _unselectAllPOIAction = new QAction(tr("Unselect all files"), this);
293 _unselectAllPOIAction->setMenuRole(QAction::NoRole);
294 _unselectAllPOIAction->setEnabled(false);
295 connect(_unselectAllPOIAction, &QAction::triggered, this,
296 &GUI::unselectAllPOIs);
297 _overlapPOIAction = new QAction(tr("Overlap POIs"), this);
298 _overlapPOIAction->setMenuRole(QAction::NoRole);
299 _overlapPOIAction->setCheckable(true);
300 connect(_overlapPOIAction, &QAction::triggered, _mapView,
301 &MapView::showOverlappedPOIs);
302 _showPOIIconsAction = new QAction(tr("Show POI icons"), this);
303 _showPOIIconsAction->setMenuRole(QAction::NoRole);
304 _showPOIIconsAction->setCheckable(true);
305 connect(_showPOIIconsAction, &QAction::triggered, _mapView,
306 &MapView::showPOIIcons);
307 _showPOILabelsAction = new QAction(tr("Show POI labels"), this);
308 _showPOILabelsAction->setMenuRole(QAction::NoRole);
309 _showPOILabelsAction->setCheckable(true);
310 connect(_showPOILabelsAction, &QAction::triggered, _mapView,
311 &MapView::showPOILabels);
312 _showPOIAction = new QAction(QIcon::fromTheme(SHOW_POI_NAME,
313 QIcon(SHOW_POI_ICON)), tr("Show POIs"), this);
314 _showPOIAction->setMenuRole(QAction::NoRole);
315 _showPOIAction->setCheckable(true);
316 _showPOIAction->setShortcut(SHOW_POI_SHORTCUT);
317 connect(_showPOIAction, &QAction::triggered, _mapView, &MapView::showPOI);
318 addAction(_showPOIAction);
320 // Map actions
321 _mapsActionGroup = new QActionGroup(this);
322 _mapsActionGroup->setExclusive(true);
323 connect(_mapsActionGroup, &QActionGroup::triggered, this, &GUI::mapChanged);
324 _showMapAction = new QAction(QIcon::fromTheme(SHOW_MAP_NAME,
325 QIcon(SHOW_MAP_ICON)), tr("Show map"), this);
326 _showMapAction->setEnabled(false);
327 _showMapAction->setMenuRole(QAction::NoRole);
328 _showMapAction->setCheckable(true);
329 _showMapAction->setShortcut(SHOW_MAP_SHORTCUT);
330 connect(_showMapAction, &QAction::triggered, _mapView,
331 &MapView::showMap);
332 addAction(_showMapAction);
333 _loadMapAction = new QAction(QIcon::fromTheme(OPEN_FILE_NAME,
334 QIcon(OPEN_FILE_ICON)), tr("Load map..."), this);
335 _loadMapAction->setMenuRole(QAction::NoRole);
336 connect(_loadMapAction, &QAction::triggered, this,
337 QOverload<>::of(&GUI::loadMap));
338 _loadMapDirAction = new QAction(QIcon::fromTheme(OPEN_DIR_NAME,
339 QIcon(OPEN_DIR_ICON)), tr("Load map directory..."), this);
340 _loadMapDirAction->setMenuRole(QAction::NoRole);
341 connect(_loadMapDirAction, &QAction::triggered, this, &GUI::loadMapDir);
342 _clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
343 _clearMapCacheAction->setEnabled(false);
344 _clearMapCacheAction->setMenuRole(QAction::NoRole);
345 connect(_clearMapCacheAction, &QAction::triggered, this,
346 &GUI::clearMapCache);
347 _nextMapAction = new QAction(tr("Next map"), this);
348 _nextMapAction->setMenuRole(QAction::NoRole);
349 _nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
350 connect(_nextMapAction, &QAction::triggered, this, &GUI::nextMap);
351 addAction(_nextMapAction);
352 _prevMapAction = new QAction(tr("Next map"), this);
353 _prevMapAction->setMenuRole(QAction::NoRole);
354 _prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
355 connect(_prevMapAction, &QAction::triggered, this, &GUI::prevMap);
356 addAction(_prevMapAction);
357 _showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this);
358 _showCoordinatesAction->setMenuRole(QAction::NoRole);
359 _showCoordinatesAction->setCheckable(true);
360 connect(_showCoordinatesAction, &QAction::triggered, _mapView,
361 &MapView::showCursorCoordinates);
363 // Position
364 _showPositionAction = new QAction(QIcon::fromTheme(SHOW_POS_NAME,
365 QIcon(SHOW_POS_ICON)), tr("Show position"), this);
366 _showPositionAction->setMenuRole(QAction::NoRole);
367 _showPositionAction->setCheckable(true);
368 _showPositionAction->setEnabled(false);
369 connect(_showPositionAction, &QAction::triggered, _mapView,
370 &MapView::showPosition);
371 _followPositionAction = new QAction(tr("Follow position"), this);
372 _followPositionAction->setMenuRole(QAction::NoRole);
373 _followPositionAction->setCheckable(true);
374 connect(_followPositionAction, &QAction::triggered, _mapView,
375 &MapView::followPosition);
376 _showPositionCoordinatesAction = new QAction(tr("Show coordinates"),
377 this);
378 _showPositionCoordinatesAction->setMenuRole(QAction::NoRole);
379 _showPositionCoordinatesAction->setCheckable(true);
380 connect(_showPositionCoordinatesAction, &QAction::triggered, _mapView,
381 &MapView::showPositionCoordinates);
382 _showMotionInfoAction = new QAction(tr("Show motion info"), this);
383 _showMotionInfoAction->setMenuRole(QAction::NoRole);
384 _showMotionInfoAction->setCheckable(true);
385 connect(_showMotionInfoAction, &QAction::triggered, _mapView,
386 &MapView::showMotionInfo);
388 // Data actions
389 _showTracksAction = new QAction(tr("Show tracks"), this);
390 _showTracksAction->setMenuRole(QAction::NoRole);
391 _showTracksAction->setCheckable(true);
392 _showTracksAction->setShortcut(SHOW_TRACKS_SHORTCUT);
393 connect(_showTracksAction, &QAction::triggered, this, &GUI::showTracks);
394 _showRoutesAction = new QAction(tr("Show routes"), this);
395 _showRoutesAction->setMenuRole(QAction::NoRole);
396 _showRoutesAction->setCheckable(true);
397 connect(_showRoutesAction, &QAction::triggered, this, &GUI::showRoutes);
398 _showWaypointsAction = new QAction(tr("Show waypoints"), this);
399 _showWaypointsAction->setMenuRole(QAction::NoRole);
400 _showWaypointsAction->setCheckable(true);
401 connect(_showWaypointsAction, &QAction::triggered, this,
402 &GUI::showWaypoints);
403 _showAreasAction = new QAction(tr("Show areas"), this);
404 _showAreasAction->setMenuRole(QAction::NoRole);
405 _showAreasAction->setCheckable(true);
406 connect(_showAreasAction, &QAction::triggered, this, &GUI::showAreas);
407 _showWaypointIconsAction = new QAction(tr("Waypoint icons"), this);
408 _showWaypointIconsAction->setMenuRole(QAction::NoRole);
409 _showWaypointIconsAction->setCheckable(true);
410 connect(_showWaypointIconsAction, &QAction::triggered, _mapView,
411 &MapView::showWaypointIcons);
412 _showWaypointLabelsAction = new QAction(tr("Waypoint labels"), this);
413 _showWaypointLabelsAction->setMenuRole(QAction::NoRole);
414 _showWaypointLabelsAction->setCheckable(true);
415 connect(_showWaypointLabelsAction, &QAction::triggered, _mapView,
416 &MapView::showWaypointLabels);
417 _showRouteWaypointsAction = new QAction(tr("Route waypoints"), this);
418 _showRouteWaypointsAction->setMenuRole(QAction::NoRole);
419 _showRouteWaypointsAction->setCheckable(true);
420 connect(_showRouteWaypointsAction, &QAction::triggered, _mapView,
421 &MapView::showRouteWaypoints);
422 _showTicksAction = new QAction(tr("km/mi markers"), this);
423 _showTicksAction->setMenuRole(QAction::NoRole);
424 _showTicksAction->setCheckable(true);
425 connect(_showTicksAction, &QAction::triggered, _mapView,
426 &MapView::showTicks);
427 QActionGroup *markerInfoGroup = new QActionGroup(this);
428 connect(markerInfoGroup, &QActionGroup::triggered, this,
429 &GUI::showPathMarkerInfo);
430 _hideMarkersAction = new QAction(tr("Do not show"), this);
431 _hideMarkersAction->setMenuRole(QAction::NoRole);
432 _hideMarkersAction->setCheckable(true);
433 _hideMarkersAction->setActionGroup(markerInfoGroup);
434 _showMarkersAction = new QAction(tr("Marker only"), this);
435 _showMarkersAction->setMenuRole(QAction::NoRole);
436 _showMarkersAction->setCheckable(true);
437 _showMarkersAction->setActionGroup(markerInfoGroup);
438 _showMarkerDateAction = new QAction(tr("Date/time"), this);
439 _showMarkerDateAction->setMenuRole(QAction::NoRole);
440 _showMarkerDateAction->setCheckable(true);
441 _showMarkerDateAction->setActionGroup(markerInfoGroup);
442 _showMarkerCoordinatesAction = new QAction(tr("Coordinates"), this);
443 _showMarkerCoordinatesAction->setMenuRole(QAction::NoRole);
444 _showMarkerCoordinatesAction->setCheckable(true);
445 _showMarkerCoordinatesAction->setActionGroup(markerInfoGroup);
446 _useStylesAction = new QAction(tr("Use styles"), this);
447 _useStylesAction->setMenuRole(QAction::NoRole);
448 _useStylesAction->setCheckable(true);
449 connect(_useStylesAction, &QAction::triggered, _mapView,
450 &MapView::useStyles);
452 // DEM actions
453 _downloadDEMAction = new QAction(tr("Download DEM data"), this);
454 _downloadDEMAction->setMenuRole(QAction::NoRole);
455 _downloadDEMAction->setEnabled(false);
456 _downloadDEMAction->setShortcut(DOWNLOAD_DEM_SHORTCUT);
457 connect(_downloadDEMAction, &QAction::triggered, this, &GUI::downloadDEM);
458 _showDEMTilesAction = new QAction(tr("Show local DEM tiles"), this);
459 _showDEMTilesAction->setMenuRole(QAction::NoRole);
460 connect(_showDEMTilesAction, &QAction::triggered, this, &GUI::showDEMTiles);
462 // Graph actions
463 _showGraphsAction = new QAction(QIcon::fromTheme(SHOW_GRAPHS_NAME,
464 QIcon(SHOW_GRAPHS_ICON)), tr("Show graphs"), this);
465 _showGraphsAction->setMenuRole(QAction::NoRole);
466 _showGraphsAction->setCheckable(true);
467 _showGraphsAction->setShortcut(SHOW_GRAPHS_SHORTCUT);
468 connect(_showGraphsAction, &QAction::triggered, this, &GUI::showGraphs);
469 addAction(_showGraphsAction);
470 ag = new QActionGroup(this);
471 ag->setExclusive(true);
472 _distanceGraphAction = new QAction(tr("Distance"), this);
473 _distanceGraphAction->setMenuRole(QAction::NoRole);
474 _distanceGraphAction->setCheckable(true);
475 _distanceGraphAction->setActionGroup(ag);
476 connect(_distanceGraphAction, &QAction::triggered, this,
477 &GUI::setDistanceGraph);
478 addAction(_distanceGraphAction);
479 _timeGraphAction = new QAction(tr("Time"), this);
480 _timeGraphAction->setMenuRole(QAction::NoRole);
481 _timeGraphAction->setCheckable(true);
482 _timeGraphAction->setActionGroup(ag);
483 connect(_timeGraphAction, &QAction::triggered, this, &GUI::setTimeGraph);
484 addAction(_timeGraphAction);
485 _showGraphGridAction = new QAction(tr("Show grid"), this);
486 _showGraphGridAction->setMenuRole(QAction::NoRole);
487 _showGraphGridAction->setCheckable(true);
488 connect(_showGraphGridAction, &QAction::triggered, this,
489 &GUI::showGraphGrids);
490 _showGraphSliderInfoAction = new QAction(tr("Show slider info"), this);
491 _showGraphSliderInfoAction->setMenuRole(QAction::NoRole);
492 _showGraphSliderInfoAction->setCheckable(true);
493 connect(_showGraphSliderInfoAction, &QAction::triggered, this,
494 &GUI::showGraphSliderInfo);
495 #ifdef Q_OS_ANDROID
496 _showGraphTabsAction = new QAction(tr("Show tabs"), this);
497 _showGraphTabsAction->setMenuRole(QAction::NoRole);
498 _showGraphTabsAction->setCheckable(true);
499 connect(_showGraphTabsAction, &QAction::triggered, this,
500 &GUI::showGraphTabs);
501 #endif // Q_OS_ANDROID
503 // Settings actions
504 #ifndef Q_OS_ANDROID
505 _showToolbarsAction = new QAction(tr("Show toolbars"), this);
506 _showToolbarsAction->setMenuRole(QAction::NoRole);
507 _showToolbarsAction->setCheckable(true);
508 connect(_showToolbarsAction, &QAction::triggered, this, &GUI::showToolbars);
509 #endif // Q_OS_ANDROID
510 ag = new QActionGroup(this);
511 ag->setExclusive(true);
512 _totalTimeAction = new QAction(tr("Total time"), this);
513 _totalTimeAction->setMenuRole(QAction::NoRole);
514 _totalTimeAction->setCheckable(true);
515 _totalTimeAction->setActionGroup(ag);
516 connect(_totalTimeAction, &QAction::triggered, this, &GUI::setTotalTime);
517 _movingTimeAction = new QAction(tr("Moving time"), this);
518 _movingTimeAction->setMenuRole(QAction::NoRole);
519 _movingTimeAction->setCheckable(true);
520 _movingTimeAction->setActionGroup(ag);
521 connect(_movingTimeAction, &QAction::triggered, this, &GUI::setMovingTime);
522 ag = new QActionGroup(this);
523 ag->setExclusive(true);
524 _metricUnitsAction = new QAction(tr("Metric"), this);
525 _metricUnitsAction->setMenuRole(QAction::NoRole);
526 _metricUnitsAction->setCheckable(true);
527 _metricUnitsAction->setActionGroup(ag);
528 connect(_metricUnitsAction, &QAction::triggered, this, &GUI::setMetricUnits);
529 _imperialUnitsAction = new QAction(tr("Imperial"), this);
530 _imperialUnitsAction->setMenuRole(QAction::NoRole);
531 _imperialUnitsAction->setCheckable(true);
532 _imperialUnitsAction->setActionGroup(ag);
533 connect(_imperialUnitsAction, &QAction::triggered, this,
534 &GUI::setImperialUnits);
535 _nauticalUnitsAction = new QAction(tr("Nautical"), this);
536 _nauticalUnitsAction->setMenuRole(QAction::NoRole);
537 _nauticalUnitsAction->setCheckable(true);
538 _nauticalUnitsAction->setActionGroup(ag);
539 connect(_nauticalUnitsAction, &QAction::triggered, this,
540 &GUI::setNauticalUnits);
541 ag = new QActionGroup(this);
542 ag->setExclusive(true);
543 _decimalDegreesAction = new QAction(tr("Decimal degrees (DD)"), this);
544 _decimalDegreesAction->setMenuRole(QAction::NoRole);
545 _decimalDegreesAction->setCheckable(true);
546 _decimalDegreesAction->setActionGroup(ag);
547 connect(_decimalDegreesAction, &QAction::triggered, this,
548 &GUI::setDecimalDegrees);
549 _degreesMinutesAction = new QAction(tr("Degrees and decimal minutes (DMM)"),
550 this);
551 _degreesMinutesAction->setMenuRole(QAction::NoRole);
552 _degreesMinutesAction->setCheckable(true);
553 _degreesMinutesAction->setActionGroup(ag);
554 connect(_degreesMinutesAction, &QAction::triggered, this,
555 &GUI::setDegreesMinutes);
556 _dmsAction = new QAction(tr("Degrees, minutes, seconds (DMS)"), this);
557 _dmsAction->setMenuRole(QAction::NoRole);
558 _dmsAction->setCheckable(true);
559 _dmsAction->setActionGroup(ag);
560 connect(_dmsAction, &QAction::triggered, this, &GUI::setDMS);
561 #ifndef Q_OS_ANDROID
562 _fullscreenAction = new QAction(QIcon::fromTheme(FULLSCREEN_NAME,
563 QIcon(FULLSCREEN_ICON)), tr("Fullscreen mode"), this);
564 _fullscreenAction->setMenuRole(QAction::NoRole);
565 _fullscreenAction->setCheckable(true);
566 _fullscreenAction->setShortcut(FULLSCREEN_SHORTCUT);
567 connect(_fullscreenAction, &QAction::triggered, this, &GUI::showFullscreen);
568 addAction(_fullscreenAction);
569 #endif // Q_OS_ANDROID
570 _openOptionsAction = new QAction(tr("Options..."), this);
571 _openOptionsAction->setMenuRole(QAction::PreferencesRole);
572 connect(_openOptionsAction, &QAction::triggered, this, &GUI::openOptions);
574 // Navigation actions
575 #ifndef Q_OS_ANDROID
576 _nextAction = new QAction(QIcon::fromTheme(NEXT_FILE_NAME,
577 QIcon(NEXT_FILE_ICON)), tr("Next"), this);
578 _nextAction->setActionGroup(_navigationActionGroup);
579 _nextAction->setMenuRole(QAction::NoRole);
580 connect(_nextAction, &QAction::triggered, this, &GUI::next);
581 _prevAction = new QAction(QIcon::fromTheme(PREV_FILE_NAME,
582 QIcon(PREV_FILE_ICON)), tr("Previous"), this);
583 _prevAction->setMenuRole(QAction::NoRole);
584 _prevAction->setActionGroup(_navigationActionGroup);
585 connect(_prevAction, &QAction::triggered, this, &GUI::prev);
586 _lastAction = new QAction(QIcon::fromTheme(LAST_FILE_NAME,
587 QIcon(LAST_FILE_ICON)), tr("Last"), this);
588 _lastAction->setMenuRole(QAction::NoRole);
589 _lastAction->setActionGroup(_navigationActionGroup);
590 connect(_lastAction, &QAction::triggered, this, &GUI::last);
591 _firstAction = new QAction(QIcon::fromTheme(FIRST_FILE_NAME,
592 QIcon(FIRST_FILE_ICON)), tr("First"), this);
593 _firstAction->setMenuRole(QAction::NoRole);
594 _firstAction->setActionGroup(_navigationActionGroup);
595 connect(_firstAction, &QAction::triggered, this, &GUI::first);
596 #endif // Q_OS_ANDROID
599 void GUI::createMapNodeMenu(const TreeNode<MapAction*> &node, QMenu *menu,
600 QAction *action)
602 for (int i = 0; i < node.childs().size(); i++) {
603 QMenu *cm = new QMenu(node.childs().at(i).name(), menu);
604 menu->insertMenu(action, cm);
605 createMapNodeMenu(node.childs().at(i), cm);
608 for (int i = 0; i < node.items().size(); i++)
609 menu->insertAction(action, node.items().at(i));
612 void GUI::createPOINodeMenu(const TreeNode<POIAction*> &node, QMenu *menu,
613 QAction *action)
615 for (int i = 0; i < node.childs().size(); i++) {
616 QMenu *cm = new QMenu(node.childs().at(i).name(), menu);
617 menu->insertMenu(action, cm);
618 createPOINodeMenu(node.childs().at(i), cm);
621 for (int i = 0; i < node.items().size(); i++)
622 menu->insertAction(action, node.items().at(i));
625 void GUI::createMenus()
627 QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
628 fileMenu->addAction(_openFileAction);
629 #ifndef Q_OS_ANDROID
630 _recentFilesMenu = fileMenu->addMenu(tr("Open recent"));
631 _recentFilesMenu->setIcon(QIcon::fromTheme(OPEN_RECENT_NAME,
632 QIcon(OPEN_RECENT_ICON)));
633 _recentFilesMenu->menuAction()->setMenuRole(QAction::NoRole);
634 _recentFilesMenu->setEnabled(false);
635 _recentFilesEnd = _recentFilesMenu->addSeparator();
636 _recentFilesMenu->addAction(_clearRecentFilesAction);
637 #endif // Q_OS_ANDROID
638 fileMenu->addAction(_openDirAction);
639 fileMenu->addSeparator();
640 #ifndef Q_OS_ANDROID
641 fileMenu->addAction(_printFileAction);
642 #endif // Q_OS_ANDROID
643 fileMenu->addAction(_exportPDFFileAction);
644 fileMenu->addAction(_exportPNGFileAction);
645 fileMenu->addSeparator();
646 fileMenu->addAction(_statisticsAction);
647 fileMenu->addSeparator();
648 fileMenu->addAction(_reloadFileAction);
649 fileMenu->addAction(_closeFileAction);
650 #if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
651 fileMenu->addSeparator();
652 fileMenu->addAction(_exitAction);
653 #endif // Q_OS_MAC + Q_OS_ANDROID
655 _mapMenu = menuBar()->addMenu(tr("&Map"));
656 _mapsEnd = _mapMenu->addSeparator();
657 _mapMenu->addAction(_loadMapAction);
658 _mapMenu->addAction(_loadMapDirAction);
659 _mapMenu->addAction(_clearMapCacheAction);
660 _mapMenu->addSeparator();
661 _mapMenu->addAction(_showCoordinatesAction);
662 _mapMenu->addSeparator();
663 _mapMenu->addAction(_showMapAction);
665 QMenu *graphMenu = menuBar()->addMenu(tr("&Graph"));
666 graphMenu->addAction(_distanceGraphAction);
667 graphMenu->addAction(_timeGraphAction);
668 graphMenu->addSeparator();
669 graphMenu->addAction(_showGraphGridAction);
670 graphMenu->addAction(_showGraphSliderInfoAction);
671 #ifdef Q_OS_ANDROID
672 graphMenu->addAction(_showGraphTabsAction);
673 #endif // Q_OS_ANDROID
674 graphMenu->addSeparator();
675 graphMenu->addAction(_showGraphsAction);
677 QMenu *dataMenu = menuBar()->addMenu(tr("&Data"));
678 dataMenu->addAction(_showWaypointIconsAction);
679 dataMenu->addAction(_showWaypointLabelsAction);
680 dataMenu->addAction(_showRouteWaypointsAction);
681 dataMenu->addAction(_showTicksAction);
682 QMenu *markerMenu = dataMenu->addMenu(tr("Position info"));
683 markerMenu->menuAction()->setMenuRole(QAction::NoRole);
684 markerMenu->addAction(_hideMarkersAction);
685 markerMenu->addAction(_showMarkersAction);
686 markerMenu->addAction(_showMarkerDateAction);
687 markerMenu->addAction(_showMarkerCoordinatesAction);
688 dataMenu->addSeparator();
689 dataMenu->addAction(_useStylesAction);
690 dataMenu->addSeparator();
691 dataMenu->addAction(_showTracksAction);
692 dataMenu->addAction(_showRoutesAction);
693 dataMenu->addAction(_showAreasAction);
694 dataMenu->addAction(_showWaypointsAction);
696 _poiMenu = menuBar()->addMenu(tr("&POI"));
697 _poisEnd = _poiMenu->addSeparator();
698 _poiMenu->addAction(_openPOIAction);
699 _poiMenu->addAction(_selectAllPOIAction);
700 _poiMenu->addAction(_unselectAllPOIAction);
701 _poiMenu->addSeparator();
702 _poiMenu->addAction(_showPOIIconsAction);
703 _poiMenu->addAction(_showPOILabelsAction);
704 _poiMenu->addAction(_overlapPOIAction);
705 _poiMenu->addSeparator();
706 _poiMenu->addAction(_showPOIAction);
708 QMenu *demMenu = menuBar()->addMenu(tr("DEM"));
709 demMenu->addAction(_showDEMTilesAction);
710 demMenu->addAction(_downloadDEMAction);
712 QMenu *positionMenu = menuBar()->addMenu(tr("Position"));
713 positionMenu->addAction(_showPositionCoordinatesAction);
714 positionMenu->addAction(_showMotionInfoAction);
715 positionMenu->addAction(_followPositionAction);
716 positionMenu->addSeparator();
717 positionMenu->addAction(_showPositionAction);
719 QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
720 QMenu *timeMenu = settingsMenu->addMenu(tr("Time"));
721 timeMenu->menuAction()->setMenuRole(QAction::NoRole);
722 timeMenu->addAction(_totalTimeAction);
723 timeMenu->addAction(_movingTimeAction);
724 QMenu *unitsMenu = settingsMenu->addMenu(tr("Units"));
725 unitsMenu->menuAction()->setMenuRole(QAction::NoRole);
726 unitsMenu->addAction(_metricUnitsAction);
727 unitsMenu->addAction(_imperialUnitsAction);
728 unitsMenu->addAction(_nauticalUnitsAction);
729 QMenu *coordinatesMenu = settingsMenu->addMenu(tr("Coordinates format"));
730 coordinatesMenu->menuAction()->setMenuRole(QAction::NoRole);
731 coordinatesMenu->addAction(_decimalDegreesAction);
732 coordinatesMenu->addAction(_degreesMinutesAction);
733 coordinatesMenu->addAction(_dmsAction);
734 settingsMenu->addSeparator();
735 #ifndef Q_OS_ANDROID
736 settingsMenu->addAction(_showToolbarsAction);
737 settingsMenu->addAction(_fullscreenAction);
738 settingsMenu->addSeparator();
739 #endif // Q_OS_ANDROID
740 settingsMenu->addAction(_openOptionsAction);
742 QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
743 helpMenu->addAction(_pathsAction);
744 #ifndef Q_OS_ANDROID
745 helpMenu->addAction(_keysAction);
746 #endif // Q_OS_ANDROID
747 helpMenu->addSeparator();
748 helpMenu->addAction(_aboutAction);
751 #ifdef Q_OS_ANDROID
752 void GUI::createNavigation()
754 _navigation = new NavigationWidget(_mapView);
756 connect(_navigation, &NavigationWidget::next, this, &GUI::next);
757 connect(_navigation, &NavigationWidget::prev, this, &GUI::prev);
759 #else // Q_OS_ANDROID
760 void GUI::createToolBars()
762 int is = style()->pixelMetric(QStyle::PM_ToolBarIconSize);
763 QSize iconSize(qMin(is, TOOLBAR_ICON_SIZE), qMin(is, TOOLBAR_ICON_SIZE));
765 #ifdef Q_OS_MAC
766 setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
767 #endif // Q_OS_MAC
769 _fileToolBar = addToolBar(tr("File"));
770 _fileToolBar->setObjectName("File");
771 _fileToolBar->setIconSize(iconSize);
772 _fileToolBar->addAction(_openFileAction);
773 _fileToolBar->addAction(_reloadFileAction);
774 _fileToolBar->addAction(_closeFileAction);
775 #ifndef Q_OS_MAC
776 _fileToolBar->addAction(_printFileAction);
777 #endif // Q_OS_MAC
779 _showToolBar = addToolBar(tr("Show"));
780 _showToolBar->setObjectName("Show");
781 _showToolBar->setIconSize(iconSize);
782 _showToolBar->addAction(_showPOIAction);
783 _showToolBar->addAction(_showMapAction);
784 _showToolBar->addAction(_showGraphsAction);
785 _showToolBar->addAction(_showPositionAction);
787 _navigationToolBar = addToolBar(tr("Navigation"));
788 _navigationToolBar->setObjectName("Navigation");
789 _navigationToolBar->setIconSize(iconSize);
790 _navigationToolBar->addAction(_firstAction);
791 _navigationToolBar->addAction(_prevAction);
792 _navigationToolBar->addAction(_nextAction);
793 _navigationToolBar->addAction(_lastAction);
795 #endif // Q_OS_ANDROID
797 void GUI::createMapView()
799 _map = new EmptyMap(this);
800 _mapView = new MapView(_map, _poi, this);
801 _mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
802 QSizePolicy::Expanding));
803 #ifdef Q_OS_ANDROID
804 _mapView->setMinimumHeight(100);
805 #else // Q_OS_ANDROID
806 _mapView->setMinimumHeight(200);
807 #endif // Q_OS_ANDROID
808 #ifdef Q_OS_WIN32
809 _mapView->setFrameShape(QFrame::NoFrame);
810 #endif // Q_OS_WIN32
813 void GUI::createGraphTabs()
815 _graphTabWidget = new QTabWidget();
816 _graphTabWidget->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
817 QSizePolicy::Preferred));
818 _graphTabWidget->setMinimumHeight(200);
819 #ifndef Q_OS_MAC
820 _graphTabWidget->setDocumentMode(true);
821 #endif // Q_OS_MAC
823 connect(_graphTabWidget, &QTabWidget::currentChanged, this,
824 &GUI::graphChanged);
826 _tabs.append(new ElevationGraph(_graphTabWidget));
827 _tabs.append(new SpeedGraph(_graphTabWidget));
828 _tabs.append(new HeartRateGraph(_graphTabWidget));
829 _tabs.append(new CadenceGraph(_graphTabWidget));
830 _tabs.append(new PowerGraph(_graphTabWidget));
831 _tabs.append(new TemperatureGraph(_graphTabWidget));
832 _tabs.append(new GearRatioGraph(_graphTabWidget));
834 for (int i = 0; i < _tabs.size(); i++)
835 connect(_tabs.at(i), &GraphTab::sliderPositionChanged, _mapView,
836 &MapView::setMarkerPosition);
839 void GUI::createStatusBar()
841 _fileNameLabel = new QLabel();
842 _distanceLabel = new QLabel();
843 _timeLabel = new QLabel();
844 _distanceLabel->setAlignment(Qt::AlignHCenter);
845 _timeLabel->setAlignment(Qt::AlignHCenter);
847 statusBar()->addPermanentWidget(_fileNameLabel, 8);
848 statusBar()->addPermanentWidget(_distanceLabel, 1);
849 statusBar()->addPermanentWidget(_timeLabel, 1);
850 statusBar()->setSizeGripEnabled(false);
853 void GUI::about()
855 QMessageBox msgBox(this);
856 QUrl homepage(APP_HOMEPAGE);
858 msgBox.setWindowTitle(tr("About GPXSee"));
859 #ifdef Q_OS_ANDROID
860 msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p>" + tr("Version %1")
861 .arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture()
862 + ", Qt " + QT_VERSION_STR + ")") + "</p><p>"
863 + tr("GPXSee is distributed under the terms of the GNU General Public "
864 "License version 3. For more info about GPXSee visit the project "
865 "homepage at %1.").arg("<a href=\"" + homepage.toString() + "\">"
866 + homepage.toString(QUrl::RemoveScheme).mid(2) + "</a>") + "</p>");
867 #else // Q_OS_ANDROID
868 msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p>" + tr("Version %1")
869 .arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture()
870 + ", Qt " + QT_VERSION_STR + ")") + "</p>");
871 msgBox.setInformativeText("<table width=\"300\"><tr><td>"
872 + tr("GPXSee is distributed under the terms of the GNU General Public "
873 "License version 3. For more info about GPXSee visit the project "
874 "homepage at %1.").arg("<a href=\"" + homepage.toString() + "\">"
875 + homepage.toString(QUrl::RemoveScheme).mid(2) + "</a>")
876 + "</td></tr></table>");
878 QIcon icon = msgBox.windowIcon();
879 QSize size = icon.actualSize(QSize(64, 64));
880 msgBox.setIconPixmap(icon.pixmap(size));
881 #endif // Q_OS_ANDROID
883 msgBox.exec();
886 #ifndef Q_OS_ANDROID
887 void GUI::keys()
889 QMessageBox msgBox(this);
891 msgBox.setWindowTitle(tr("Keyboard controls"));
892 msgBox.setText("<h3>" + tr("Keyboard controls") + "</h3>");
893 msgBox.setInformativeText(
894 "<style>td {padding-right: 1.5em;}</style><div><table><tr><td>"
895 + tr("Next file") + "</td><td><i>" + QKeySequence(NEXT_KEY).toString()
896 + "</i></td></tr><tr><td>" + tr("Previous file")
897 + "</td><td><i>" + QKeySequence(PREV_KEY).toString()
898 + "</i></td></tr><tr><td>" + tr("First file") + "</td><td><i>"
899 + QKeySequence(FIRST_KEY).toString() + "</i></td></tr><tr><td>"
900 + tr("Last file") + "</td><td><i>" + QKeySequence(LAST_KEY).toString()
901 + "</i></td></tr><tr><td>" + tr("Append file")
902 + "</td><td><i>" + QKeySequence(MODIFIER).toString() + tr("Next/Previous")
903 + "</i></td></tr><tr><td></td><td></td></tr><tr><td>"
904 + tr("Toggle graph type") + "</td><td><i>"
905 + QKeySequence(TOGGLE_GRAPH_TYPE_KEY).toString() + "</i></td></tr><tr><td>"
906 + tr("Toggle time type") + "</td><td><i>"
907 + QKeySequence(TOGGLE_TIME_TYPE_KEY).toString() + "</i></td></tr><tr><td>"
908 + tr("Toggle position info") + "</td><td><i>"
909 + QKeySequence(TOGGLE_MARKER_INFO_KEY).toString() + "</i></td></tr>"
910 + "<tr><td></td><td></td></tr><tr><td>" + tr("Next map")
911 + "</td><td><i>" + NEXT_MAP_SHORTCUT.toString() + "</i></td></tr><tr><td>"
912 + tr("Previous map") + "</td><td><i>" + PREV_MAP_SHORTCUT.toString()
913 + "</i></td></tr><tr><td></td><td></td></tr><tr><td>" + tr("Zoom in")
914 + "</td><td><i>" + QKeySequence(ZOOM_IN).toString()
915 + "</i></td></tr><tr><td>" + tr("Zoom out") + "</td><td><i>"
916 + QKeySequence(ZOOM_OUT).toString() + "</i></td></tr><tr><td>"
917 + tr("Digital zoom") + "</td><td><i>" + QKeySequence(MODIFIER).toString()
918 + tr("Zoom") + "</i></td></tr><tr><td></td><td></td></tr><tr><td>"
919 + tr("Copy coordinates") + "</td><td><i>"
920 + QKeySequence(MODIFIER).toString() + tr("Left Click")
921 + "</i></td></tr></table></div>");
923 msgBox.exec();
925 #endif // Q_OS_ANDROID
927 void GUI::paths()
929 QMessageBox msgBox(this);
931 msgBox.setWindowTitle(tr("Paths"));
932 #ifdef Q_OS_ANDROID
933 msgBox.setText(
934 + "<small><b>" + tr("Map directory:") + "</b><br>"
935 + QDir::cleanPath(ProgramPaths::mapDir(true)) + "<br><br><b>"
936 + tr("POI directory:") + "</b><br>"
937 + QDir::cleanPath(ProgramPaths::poiDir(true)) + "<br><br><b>"
938 + tr("CRS directory:") + "</b><br>"
939 + QDir::cleanPath(ProgramPaths::crsDir(true)) + "<br><br><b>"
940 + tr("DEM directory:") + "</b><br>"
941 + QDir::cleanPath(ProgramPaths::demDir(true)) + "<br><br><b>"
942 + tr("Styles directory:") + "</b><br>"
943 + QDir::cleanPath(ProgramPaths::styleDir(true)) + "<br><br><b>"
944 + tr("Symbols directory:") + "</b><br>"
945 + QDir::cleanPath(ProgramPaths::symbolsDir(true)) + "<br><br><b>"
946 + tr("Tile cache directory:") + "</b><br>"
947 + QDir::cleanPath(ProgramPaths::tilesDir()) + "</small>");
948 #else // Q_OS_ANDROID
949 msgBox.setText("<h3>" + tr("Paths") + "</h3>");
950 msgBox.setInformativeText(
951 "<style>td {white-space: pre; padding-right: 1em;}</style><table><tr><td>"
952 + tr("Map directory:") + "</td><td><code>"
953 + QDir::cleanPath(ProgramPaths::mapDir(true)) + "</code></td></tr><tr><td>"
954 + tr("POI directory:") + "</td><td><code>"
955 + QDir::cleanPath(ProgramPaths::poiDir(true)) + "</code></td></tr><tr><td>"
956 + tr("CRS directory:") + "</td><td><code>"
957 + QDir::cleanPath(ProgramPaths::crsDir(true)) + "</code></td></tr><tr><td>"
958 + tr("DEM directory:") + "</td><td><code>"
959 + QDir::cleanPath(ProgramPaths::demDir(true)) + "</code></td></tr><tr><td>"
960 + tr("Styles directory:") + "</td><td><code>"
961 + QDir::cleanPath(ProgramPaths::styleDir(true)) + "</code></td></tr><tr><td>"
962 + tr("Symbols directory:") + "</td><td><code>"
963 + QDir::cleanPath(ProgramPaths::symbolsDir(true)) + "</code></td></tr><tr><td>"
964 + tr("Tile cache directory:") + "</td><td><code>"
965 + QDir::cleanPath(ProgramPaths::tilesDir()) + "</code></td></tr></table>"
967 #endif // Q_OS_ANDROID
969 msgBox.exec();
972 void GUI::openFile()
974 #ifdef Q_OS_ANDROID
975 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"),
976 _dataDir));
977 #else // Q_OS_ANDROID
978 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"),
979 _dataDir, Data::formats()));
980 #endif // Q_OS_ANDROID
981 int showError = (files.size() > 1) ? 2 : 1;
983 for (int i = 0; i < files.size(); i++)
984 openFile(files.at(i), true, showError);
985 if (!files.isEmpty())
986 _dataDir = QFileInfo(files.last()).path();
989 #ifndef Q_OS_ANDROID
990 void GUI::openDir(const QString &path, int &showError)
992 QDir md(path);
993 md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
994 md.setSorting(QDir::DirsLast);
995 QFileInfoList ml = md.entryInfoList();
997 for (int i = 0; i < ml.size(); i++) {
998 const QFileInfo &fi = ml.at(i);
1000 if (fi.isDir())
1001 openDir(fi.absoluteFilePath(), showError);
1002 else
1003 openFile(fi.absoluteFilePath(), true, showError);
1006 #endif // Q_OS_ANDROID
1008 void GUI::openDir()
1010 QString dir(QFileDialog::getExistingDirectory(this, tr("Open directory"),
1011 _dataDir));
1013 if (!dir.isEmpty()) {
1014 #ifdef Q_OS_ANDROID
1015 int showError = 1;
1016 _browser->setCurrentDir(dir);
1017 openFile(_browser->current(), true, showError);
1018 #else // Q_OS_ANDROID
1019 int showError = 2;
1020 openDir(dir, showError);
1021 _dataDir = dir;
1022 #endif // Q_OS_ANDROID
1026 bool GUI::openFile(const QString &fileName, bool tryUnknown, int &showError)
1028 if (_files.contains(fileName))
1029 return true;
1031 if (!loadFile(fileName, tryUnknown, showError))
1032 return false;
1034 _files.append(fileName);
1035 #ifndef Q_OS_ANDROID
1036 _browser->setCurrent(fileName);
1037 #endif // Q_OS_ANDROID
1038 _fileActionGroup->setEnabled(true);
1039 // Explicitly enable the reload action as it may be disabled by loadMapDir()
1040 _reloadFileAction->setEnabled(true);
1041 _navigationActionGroup->setEnabled(true);
1043 updateNavigationActions();
1044 updateStatusBarInfo();
1045 updateWindowTitle();
1046 #ifndef Q_OS_ANDROID
1047 updateRecentFiles(fileName);
1048 #endif // Q_OS_ANDROID
1050 return true;
1053 bool GUI::loadFile(const QString &fileName, bool tryUnknown, int &showError)
1055 Data data(fileName, tryUnknown);
1057 if (data.isValid()) {
1058 loadData(data);
1059 return true;
1060 } else {
1061 updateNavigationActions();
1062 updateStatusBarInfo();
1063 updateWindowTitle();
1064 updateGraphTabs();
1065 updateDEMDownloadAction();
1066 if (_files.isEmpty())
1067 _fileActionGroup->setEnabled(false);
1069 if (showError) {
1070 QString error = tr("Error loading data file:") + "\n"
1071 + Util::displayName(fileName) + ": " + data.errorString();
1072 if (data.errorLine())
1073 error.append("\n" + tr("Line: %1").arg(data.errorLine()));
1075 if (showError > 1) {
1076 QMessageBox message(QMessageBox::Critical, APP_NAME, error,
1077 QMessageBox::Ok, this);
1078 QCheckBox checkBox(tr("Don't show again"));
1079 message.setCheckBox(&checkBox);
1080 message.exec();
1081 if (checkBox.isChecked())
1082 showError = 0;
1083 } else
1084 QMessageBox::critical(this, APP_NAME, error);
1087 return false;
1091 void GUI::loadData(const Data &data)
1093 QList<QList<GraphItem*> > graphs;
1094 QList<PathItem*> paths;
1096 for (int i = 0; i < data.tracks().count(); i++) {
1097 const Track &track = data.tracks().at(i);
1098 _trackDistance += track.distance();
1099 _time += track.time();
1100 _movingTime += track.movingTime();
1101 const QDateTime date = track.date().toTimeZone(_options.timeZone.zone());
1102 if (_dateRange.first.isNull() || _dateRange.first > date)
1103 _dateRange.first = date;
1104 if (_dateRange.second.isNull() || _dateRange.second < date)
1105 _dateRange.second = date;
1107 _trackCount += data.tracks().count();
1109 for (int i = 0; i < data.routes().count(); i++)
1110 _routeDistance += data.routes().at(i).distance();
1111 _routeCount += data.routes().count();
1113 _waypointCount += data.waypoints().count();
1114 _areaCount += data.areas().count();
1116 if (_pathName.isNull()) {
1117 if (data.tracks().count() == 1 && !data.routes().count())
1118 _pathName = data.tracks().first().name();
1119 else if (data.routes().count() == 1 && !data.tracks().count())
1120 _pathName = data.routes().first().name();
1121 } else
1122 _pathName = QString();
1124 for (int i = 0; i < _tabs.count(); i++)
1125 graphs.append(_tabs.at(i)->loadData(data));
1126 if (updateGraphTabs())
1127 _splitter->refresh();
1128 paths = _mapView->loadData(data);
1130 GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->currentWidget());
1132 for (int i = 0; i < paths.count(); i++) {
1133 PathItem *pi = paths.at(i);
1134 if (!pi)
1135 continue;
1137 for (int j = 0; j < graphs.count(); j++)
1138 pi->addGraph(graphs.at(j).at(i));
1140 if (gt) {
1141 pi->setGraph(_tabs.indexOf(gt));
1142 pi->setMarkerPosition(gt->sliderPosition());
1146 updateDEMDownloadAction();
1149 void GUI::openPOIFile()
1151 #ifdef Q_OS_ANDROID
1152 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"),
1153 _poiDir));
1154 #else // Q_OS_ANDROID
1155 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"),
1156 _poiDir, Data::formats()));
1157 #endif // Q_OS_ANDROID
1159 for (int i = 0; i < files.size(); i++)
1160 openPOIFile(files.at(i));
1161 if (!files.isEmpty())
1162 _poiDir = QFileInfo(files.last()).path();
1165 bool GUI::openPOIFile(const QString &fileName)
1167 if (_poi->isLoaded(fileName))
1168 return true;
1170 if (_poi->loadFile(fileName)) {
1171 _mapView->showPOI(true);
1172 _showPOIAction->setChecked(true);
1173 QAction *action = new POIAction(fileName, _poisActionGroup);
1174 action->setChecked(true);
1175 _poiMenu->insertAction(_poisEnd, action);
1177 _selectAllPOIAction->setEnabled(true);
1178 _unselectAllPOIAction->setEnabled(true);
1180 return true;
1181 } else {
1182 QString error = tr("Error loading POI file:") + "\n"
1183 + Util::displayName(fileName) + ": " + _poi->errorString();
1184 if (_poi->errorLine())
1185 error.append("\n" + tr("Line: %1").arg(_poi->errorLine()));
1186 QMessageBox::critical(this, APP_NAME, error);
1188 return false;
1192 void GUI::openOptions()
1194 Options options(_options);
1195 OptionsDialog dialog(options, _units, this);
1197 if (dialog.exec() != QDialog::Accepted)
1198 return;
1200 updateOptions(options);
1203 void GUI::printFile()
1205 QPrinter printer(QPrinter::HighResolution);
1206 QPrintDialog dialog(&printer, this);
1208 if (dialog.exec() == QDialog::Accepted)
1209 plot(&printer);
1212 void GUI::exportPDFFile()
1214 PDFExportDialog dialog(_pdfExport, _units, this);
1215 if (dialog.exec() != QDialog::Accepted)
1216 return;
1218 QPrinter printer(QPrinter::HighResolution);
1219 printer.setOutputFormat(QPrinter::PdfFormat);
1220 printer.setCreator(QString(APP_NAME) + QString(" ")
1221 + QString(APP_VERSION));
1222 printer.setResolution(_pdfExport.resolution);
1223 printer.setPageLayout(QPageLayout(QPageSize(_pdfExport.paperSize),
1224 _pdfExport.orientation, _pdfExport.margins, QPageLayout::Millimeter));
1225 printer.setOutputFileName(_pdfExport.fileName);
1227 plot(&printer);
1230 void GUI::exportPNGFile()
1232 PNGExportDialog dialog(_pngExport, this);
1233 if (dialog.exec() != QDialog::Accepted)
1234 return;
1236 QImage img(_pngExport.size, QImage::Format_ARGB32_Premultiplied);
1237 QPainter p(&img);
1238 QRectF rect(0, 0, img.width(), img.height());
1239 QRectF contentRect(rect.adjusted(_pngExport.margins.left(),
1240 _pngExport.margins.top(), -_pngExport.margins.right(),
1241 -_pngExport.margins.bottom()));
1243 if (_pngExport.antialiasing)
1244 p.setRenderHint(QPainter::Antialiasing);
1245 p.fillRect(rect, Qt::white);
1246 plotMainPage(&p, contentRect, 1.0, true);
1247 img.save(_pngExport.fileName, "png");
1249 if (!_tabs.isEmpty() && _options.separateGraphPage) {
1250 QImage img2(_pngExport.size.width(), (int)graphPlotHeight(rect, 1)
1251 + _pngExport.margins.bottom(), QImage::Format_ARGB32_Premultiplied);
1252 QPainter p2(&img2);
1253 QRectF rect2(0, 0, img2.width(), img2.height());
1255 if (_pngExport.antialiasing)
1256 p2.setRenderHint(QPainter::Antialiasing);
1257 p2.fillRect(rect2, Qt::white);
1258 plotGraphsPage(&p2, contentRect, 1);
1260 QFileInfo fi(_pngExport.fileName);
1261 img2.save(fi.absolutePath() + "/" + fi.baseName() + "-graphs."
1262 + fi.suffix(), "png");
1266 void GUI::statistics()
1268 QLocale l(QLocale::system());
1269 QMessageBox msgBox(this);
1270 QString text;
1272 #ifdef Q_OS_ANDROID
1273 if (_showTracksAction->isChecked() && _trackCount > 1)
1274 text.append("<b>" + tr("Tracks") + ":</b> "
1275 + l.toString(_trackCount) + "<br>");
1276 if (_showRoutesAction->isChecked() && _routeCount > 1)
1277 text.append("<b>" + tr("Routes") + ":</b> "
1278 + l.toString(_routeCount) + "<br>");
1279 if (_showWaypointsAction->isChecked() && _waypointCount > 1)
1280 text.append("<b>" + tr("Waypoints") + ":</b> "
1281 + l.toString(_waypointCount) + "<br>");
1282 if (_showAreasAction->isChecked() && _areaCount > 1)
1283 text.append("<b>" + tr("Areas") + ":</b> "
1284 + l.toString(_areaCount) + "<br>");
1286 if (_dateRange.first.isValid()) {
1287 QString format = l.dateFormat(QLocale::ShortFormat);
1288 if (_dateRange.first == _dateRange.second)
1289 text.append("<b>" + tr("Date") + ":</b> "
1290 + _dateRange.first.toString(format) + "<br>");
1291 else
1292 text.append("<b>" + tr("Date") + ":</b> "
1293 + QString("%1 - %2").arg(_dateRange.first.toString(format),
1294 _dateRange.second.toString(format)) + "<br>");
1297 if (distance() > 0)
1298 text.append("<b>" + tr("Distance") + ":</b> "
1299 + Format::distance(distance(), units()) + "<br>");
1300 if (time() > 0) {
1301 text.append("<b>" + tr("Time") + ":</b> "
1302 + Format::timeSpan(time()) + "<br>");
1303 text.append("<b>" + tr("Moving time") + ":</b> "
1304 + Format::timeSpan(movingTime()) + "<br>");
1306 text.append("<br>");
1308 for (int i = 0; i < _tabs.count(); i++) {
1309 const GraphTab *tab = _tabs.at(i);
1310 if (tab->isEmpty())
1311 continue;
1313 text.append("<i>" + tab->label() + "</i><br>");
1314 for (int j = 0; j < tab->info().size(); j++) {
1315 const KV<QString, QString> &kv = tab->info().at(j);
1316 text.append("<b>" + kv.key() + ":</b>&nbsp;" + kv.value());
1317 if (j != tab->info().size() - 1)
1318 text.append(" | ");
1320 if (i != _tabs.count() - 1)
1321 text.append("<br><br>");
1324 msgBox.setWindowTitle(tr("Statistics"));
1325 msgBox.setText(text);
1327 #else // Q_OS_ANDROID
1329 #ifdef Q_OS_WIN32
1330 text = "<style>td {white-space: pre; padding-right: 4em;}"
1331 "th {text-align: left; padding-top: 0.5em;}</style><table>";
1332 #else // Q_OS_WIN32
1333 text = "<style>td {white-space: pre; padding-right: 2em;}"
1334 "th {text-align: left; padding-top: 0.5em;}</style><table>";
1335 #endif // Q_OS_WIN32
1337 if (_showTracksAction->isChecked() && _trackCount > 1)
1338 text.append("<tr><td>" + tr("Tracks") + ":</td><td>"
1339 + l.toString(_trackCount) + "</td></tr>");
1340 if (_showRoutesAction->isChecked() && _routeCount > 1)
1341 text.append("<tr><td>" + tr("Routes") + ":</td><td>"
1342 + l.toString(_routeCount) + "</td></tr>");
1343 if (_showWaypointsAction->isChecked() && _waypointCount > 1)
1344 text.append("<tr><td>" + tr("Waypoints") + ":</td><td>"
1345 + l.toString(_waypointCount) + "</td></tr>");
1346 if (_showAreasAction->isChecked() && _areaCount > 1)
1347 text.append("<tr><td>" + tr("Areas") + ":</td><td>"
1348 + l.toString(_areaCount) + "</td></tr>");
1350 if (_dateRange.first.isValid()) {
1351 if (_dateRange.first == _dateRange.second) {
1352 QString format = l.dateFormat(QLocale::LongFormat);
1353 text.append("<tr><td>" + tr("Date") + ":</td><td>"
1354 + _dateRange.first.toString(format) + "</td></tr>");
1355 } else {
1356 QString format = l.dateFormat(QLocale::ShortFormat);
1357 text.append("<tr><td>" + tr("Date") + ":</td><td>"
1358 + QString("%1 - %2").arg(_dateRange.first.toString(format),
1359 _dateRange.second.toString(format)) + "</td></tr>");
1363 if (distance() > 0)
1364 text.append("<tr><td>" + tr("Distance") + ":</td><td>"
1365 + Format::distance(distance(), units()) + "</td></tr>");
1366 if (time() > 0) {
1367 text.append("<tr><td>" + tr("Time") + ":</td><td>"
1368 + Format::timeSpan(time()) + "</td></tr>");
1369 text.append("<tr><td>" + tr("Moving time") + ":</td><td>"
1370 + Format::timeSpan(movingTime()) + "</td></tr>");
1373 for (int i = 0; i < _tabs.count(); i++) {
1374 const GraphTab *tab = _tabs.at(i);
1375 if (tab->isEmpty())
1376 continue;
1378 text.append("<tr><th colspan=\"2\">" + tab->label() + "</th></tr>");
1379 for (int j = 0; j < tab->info().size(); j++) {
1380 const KV<QString, QString> &kv = tab->info().at(j);
1381 text.append("<tr><td>" + kv.key() + ":</td><td>" + kv.value()
1382 + "</td></tr>");
1386 text.append("</table>");
1388 msgBox.setWindowTitle(tr("Statistics"));
1389 msgBox.setText("<h3>" + tr("Statistics") + "</h3>");
1390 msgBox.setInformativeText(text);
1391 #endif // Q_OS_ANDROID
1393 msgBox.exec();
1396 void GUI::plotMainPage(QPainter *painter, const QRectF &rect, qreal ratio,
1397 bool expand)
1399 QLocale l(QLocale::system());
1400 TrackInfo info;
1401 qreal ih, gh, mh;
1402 int sc;
1405 if (!_pathName.isNull() && _options.printName)
1406 info.insert(tr("Name"), _pathName);
1408 if (_options.printItemCount) {
1409 if (_showTracksAction->isChecked() && _trackCount > 1)
1410 info.insert(tr("Tracks"), l.toString(_trackCount));
1411 if (_showRoutesAction->isChecked() && _routeCount > 1)
1412 info.insert(tr("Routes"), l.toString(_routeCount));
1413 if (_showWaypointsAction->isChecked() && _waypointCount > 1)
1414 info.insert(tr("Waypoints"), l.toString(_waypointCount));
1415 if (_showAreasAction->isChecked() && _areaCount > 1)
1416 info.insert(tr("Areas"), l.toString(_areaCount));
1419 if (_dateRange.first.isValid() && _options.printDate) {
1420 if (_dateRange.first == _dateRange.second) {
1421 QString format = l.dateFormat(QLocale::LongFormat);
1422 info.insert(tr("Date"), _dateRange.first.toString(format));
1423 } else {
1424 QString format = l.dateFormat(QLocale::ShortFormat);
1425 info.insert(tr("Date"), QString("%1 - %2")
1426 .arg(_dateRange.first.toString(format),
1427 _dateRange.second.toString(format)));
1431 if (distance() > 0 && _options.printDistance)
1432 info.insert(tr("Distance"), Format::distance(distance(), units()));
1433 if (time() > 0 && _options.printTime)
1434 info.insert(tr("Time"), Format::timeSpan(time()));
1435 if (movingTime() > 0 && _options.printMovingTime)
1436 info.insert(tr("Moving time"), Format::timeSpan(movingTime()));
1438 if (info.isEmpty()) {
1439 ih = 0;
1440 mh = 0;
1441 } else {
1442 ih = info.contentSize().height() * ratio;
1443 mh = ih / 2;
1444 info.plot(painter, QRectF(rect.x(), rect.y(), rect.width(), ih), ratio);
1446 if (_graphTabWidget->isVisible() && !_options.separateGraphPage) {
1447 qreal r = rect.width() / rect.height();
1448 gh = (rect.width() > rect.height())
1449 ? 0.15 * r * (rect.height() - ih - 2*mh)
1450 : 0.15 * (rect.height() - ih - 2*mh);
1451 if (gh < 150)
1452 gh = 150;
1453 sc = 2;
1454 GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->currentWidget());
1455 gt->plot(painter, QRectF(rect.x(), rect.y() + rect.height() - gh,
1456 rect.width(), gh), ratio);
1457 } else {
1458 gh = 0;
1459 sc = 1;
1462 MapView::PlotFlags flags;
1463 if (_options.hiresPrint)
1464 flags |= MapView::HiRes;
1465 if (expand)
1466 flags |= MapView::Expand;
1468 _mapView->plot(painter, QRectF(rect.x(), rect.y() + ih + mh, rect.width(),
1469 rect.height() - (ih + sc*mh + gh)), ratio, flags);
1472 void GUI::plotGraphsPage(QPainter *painter, const QRectF &rect, qreal ratio)
1474 int cnt = 0;
1475 for (int i = 0; i < _tabs.size(); i++)
1476 if (!_tabs.at(i)->isEmpty())
1477 cnt++;
1479 qreal sp = ratio * 20;
1480 qreal gh = qMin((rect.height() - ((cnt - 1) * sp))/(qreal)cnt,
1481 0.20 * rect.height());
1483 qreal y = 0;
1484 for (int i = 0; i < _tabs.size(); i++) {
1485 if (!_tabs.at(i)->isEmpty()) {
1486 _tabs.at(i)->plot(painter, QRectF(rect.x(), rect.y() + y,
1487 rect.width(), gh), ratio);
1488 y += gh + sp;
1493 qreal GUI::graphPlotHeight(const QRectF &rect, qreal ratio)
1495 int cnt = 0;
1496 for (int i = 0; i < _tabs.size(); i++)
1497 if (!_tabs.at(i)->isEmpty())
1498 cnt++;
1500 qreal sp = ratio * 20;
1501 qreal gh = qMin((rect.height() - ((cnt - 1) * sp))/(qreal)cnt,
1502 0.20 * rect.height());
1504 return cnt * gh + (cnt - 1) * sp;
1507 void GUI::plot(QPrinter *printer)
1509 QPainter p(printer);
1510 qreal fsr = 1085.0 / (qMax(printer->width(), printer->height())
1511 / (qreal)printer->resolution());
1512 qreal ratio = p.paintEngine()->paintDevice()->logicalDpiX() / fsr;
1513 QRectF rect(0, 0, printer->width(), printer->height());
1515 plotMainPage(&p, rect, ratio);
1517 if (!_tabs.isEmpty() && _options.separateGraphPage) {
1518 printer->newPage();
1519 plotGraphsPage(&p, rect, ratio);
1523 void GUI::reloadFiles()
1525 _trackCount = 0;
1526 _routeCount = 0;
1527 _waypointCount = 0;
1528 _areaCount = 0;
1529 _trackDistance = 0;
1530 _routeDistance = 0;
1531 _time = 0;
1532 _movingTime = 0;
1533 _dateRange = DateTimeRange(QDateTime(), QDateTime());
1534 _pathName = QString();
1536 for (int i = 0; i < _tabs.count(); i++)
1537 _tabs.at(i)->clear();
1538 _mapView->clear();
1540 int showError = 2;
1541 for (int i = 0; i < _files.size(); i++) {
1542 if (!loadFile(_files.at(i), true, showError)) {
1543 _files.removeAt(i);
1544 i--;
1548 updateStatusBarInfo();
1549 updateWindowTitle();
1550 if (_files.isEmpty())
1551 _fileActionGroup->setEnabled(false);
1552 #ifndef Q_OS_ANDROID
1553 else
1554 _browser->setCurrent(_files.last());
1555 #endif // Q_OS_ANDROID
1556 updateDEMDownloadAction();
1559 void GUI::closeFiles()
1561 _trackCount = 0;
1562 _routeCount = 0;
1563 _waypointCount = 0;
1564 _areaCount = 0;
1565 _trackDistance = 0;
1566 _routeDistance = 0;
1567 _time = 0;
1568 _movingTime = 0;
1569 _dateRange = DateTimeRange(QDateTime(), QDateTime());
1570 _pathName = QString();
1572 for (int i = 0; i < _tabs.count(); i++)
1573 _tabs.at(i)->clear();
1574 _lastTab = 0;
1576 _mapView->clear();
1578 _files.clear();
1581 void GUI::closeAll()
1583 closeFiles();
1585 _fileActionGroup->setEnabled(false);
1586 updateStatusBarInfo();
1587 updateWindowTitle();
1588 updateGraphTabs();
1589 updateDEMDownloadAction();
1591 #ifdef Q_OS_ANDROID
1592 _browser->setCurrentDir(QString());
1593 #endif // Q_OS_ANDROID
1596 void GUI::showGraphs(bool show)
1598 _graphTabWidget->setHidden(!show);
1601 #ifdef Q_OS_ANDROID
1602 void GUI::showGraphTabs(bool show)
1604 _graphTabWidget->tabBar()->setVisible(show);
1606 #else // Q_OS_ANDROID
1607 void GUI::showToolbars(bool show)
1609 if (show) {
1610 Q_ASSERT(!_windowStates.isEmpty());
1611 restoreState(_windowStates.last());
1612 _windowStates.removeLast();
1613 } else {
1614 _windowStates.append(saveState());
1615 removeToolBar(_fileToolBar);
1616 removeToolBar(_showToolBar);
1617 removeToolBar(_navigationToolBar);
1621 void GUI::showFullscreen(bool show)
1623 if (show) {
1624 _windowGeometries.append(saveGeometry());
1625 _frameStyle = _mapView->frameStyle();
1626 statusBar()->hide();
1627 menuBar()->hide();
1628 showToolbars(false);
1629 _mapView->setFrameStyle(QFrame::NoFrame);
1630 _graphTabWidget->tabBar()->hide();
1631 #ifdef Q_OS_MAC
1632 _graphTabWidget->setDocumentMode(true);
1633 #endif // Q_OS_MAC
1634 showFullScreen();
1635 } else {
1636 Q_ASSERT(!_windowGeometries.isEmpty());
1637 _windowGeometries.removeLast();
1638 statusBar()->show();
1639 menuBar()->show();
1640 showToolbars(true);
1641 _mapView->setFrameStyle(_frameStyle);
1642 _graphTabWidget->tabBar()->show();
1643 #ifdef Q_OS_MAC
1644 _graphTabWidget->setDocumentMode(false);
1645 #endif // Q_OS_MAC
1646 showNormal();
1649 #endif // Q_OS_ANDROID
1651 void GUI::showTracks(bool show)
1653 _mapView->showTracks(show);
1655 for (int i = 0; i < _tabs.size(); i++)
1656 _tabs.at(i)->showTracks(show);
1658 updateStatusBarInfo();
1659 updateGraphTabs();
1660 updateDEMDownloadAction();
1663 void GUI::showRoutes(bool show)
1665 _mapView->showRoutes(show);
1667 for (int i = 0; i < _tabs.size(); i++)
1668 _tabs.at(i)->showRoutes(show);
1670 updateStatusBarInfo();
1671 updateGraphTabs();
1672 updateDEMDownloadAction();
1675 void GUI::showWaypoints(bool show)
1677 _mapView->showWaypoints(show);
1678 updateDEMDownloadAction();
1681 void GUI::showAreas(bool show)
1683 _mapView->showAreas(show);
1684 updateDEMDownloadAction();
1687 void GUI::showGraphGrids(bool show)
1689 for (int i = 0; i < _tabs.size(); i++)
1690 _tabs.at(i)->showGrid(show);
1693 void GUI::showGraphSliderInfo(bool show)
1695 for (int i = 0; i < _tabs.size(); i++)
1696 _tabs.at(i)->showSliderInfo(show);
1699 void GUI::showPathMarkerInfo(QAction *action)
1701 if (action == _showMarkersAction) {
1702 _mapView->showMarkers(true);
1703 _mapView->showMarkerInfo(MarkerInfoItem::None);
1704 } else if (action == _showMarkerDateAction) {
1705 _mapView->showMarkers(true);
1706 _mapView->showMarkerInfo(MarkerInfoItem::Date);
1707 } else if (action == _showMarkerCoordinatesAction) {
1708 _mapView->showMarkers(true);
1709 _mapView->showMarkerInfo(MarkerInfoItem::Position);
1710 } else {
1711 _mapView->showMarkers(false);
1712 _mapView->showMarkerInfo(MarkerInfoItem::None);
1716 void GUI::loadMap()
1718 #ifdef Q_OS_ANDROID
1719 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"),
1720 _mapDir));
1721 #else // Q_OS_ANDROID
1722 QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"),
1723 _mapDir, MapList::formats()));
1724 #endif // Q_OS_ANDROID
1725 MapAction *a, *lastReady = 0;
1726 int showError = (files.size() > 1) ? 2 : 1;
1728 for (int i = 0; i < files.size(); i++) {
1729 if (loadMap(files.at(i), a, showError) && a)
1730 lastReady = a;
1732 if (!files.isEmpty())
1733 _mapDir = QFileInfo(files.last()).path();
1734 if (lastReady)
1735 lastReady->trigger();
1738 static MapAction *findMapAction(const QList<QAction*> &mapActions,
1739 const Map *map)
1741 for (int i = 0; i < mapActions.count(); i++) {
1742 const Map *m = mapActions.at(i)->data().value<Map*>();
1743 if (map->path() == m->path())
1744 return static_cast<MapAction*>(mapActions.at(i));
1747 return 0;
1750 bool GUI::loadMapNode(const TreeNode<Map*> &node, MapAction *&action,
1751 const QList<QAction*> &existingActions, int &showError)
1753 bool valid = false;
1755 action = 0;
1757 for (int i = 0; i < node.childs().size(); i++)
1758 valid = loadMapNode(node.childs().at(i), action, existingActions,
1759 showError);
1761 for (int i = 0; i < node.items().size(); i++) {
1762 Map *map = node.items().at(i);
1763 MapAction *a;
1765 if (!(a = findMapAction(existingActions, map))) {
1766 if (!map->isValid()) {
1767 if (showError) {
1768 QString error = tr("Error loading map:") + "\n"
1769 + Util::displayName(map->path()) + ": " + map->errorString();
1771 if (showError > 1) {
1772 QMessageBox message(QMessageBox::Critical, APP_NAME,
1773 error, QMessageBox::Ok, this);
1774 QCheckBox checkBox(tr("Don't show again"));
1775 message.setCheckBox(&checkBox);
1776 message.exec();
1777 if (checkBox.isChecked())
1778 showError = 0;
1779 } else
1780 QMessageBox::critical(this, APP_NAME, error);
1783 delete map;
1784 } else {
1785 valid = true;
1786 a = new MapAction(map, _mapsActionGroup);
1787 _mapMenu->insertAction(_mapsEnd, a);
1789 if (map->isReady()) {
1790 action = a;
1791 _showMapAction->setEnabled(true);
1792 _clearMapCacheAction->setEnabled(true);
1793 } else
1794 connect(a, &MapAction::loaded, this, &GUI::mapLoaded);
1796 } else {
1797 valid = true;
1798 map = a->data().value<Map*>();
1799 if (map->isReady())
1800 action = a;
1804 return valid;
1807 bool GUI::loadMap(const QString &fileName, MapAction *&action, int &showError)
1809 TreeNode<Map*> maps(MapList::loadMaps(fileName, _mapView->inputProjection()));
1810 QList<QAction*> existingActions(_mapsActionGroup->actions());
1812 return loadMapNode(maps, action, existingActions, showError);
1815 void GUI::mapLoaded()
1817 MapAction *action = static_cast<MapAction*>(QObject::sender());
1818 Map *map = action->data().value<Map*>();
1820 if (map->isValid()) {
1821 action->trigger();
1822 _showMapAction->setEnabled(true);
1823 _clearMapCacheAction->setEnabled(true);
1824 } else {
1825 QString error = tr("Error loading map:") + "\n"
1826 + Util::displayName(map->path()) + ": " + map->errorString();
1827 QMessageBox::critical(this, APP_NAME, error);
1828 action->deleteLater();
1832 void GUI::mapLoadedDir()
1834 MapAction *action = static_cast<MapAction*>(QObject::sender());
1835 Map *map = action->data().value<Map*>();
1837 if (map->isValid()) {
1838 _showMapAction->setEnabled(true);
1839 _clearMapCacheAction->setEnabled(true);
1840 QList<MapAction*> actions;
1841 actions.append(action);
1842 _mapView->loadMaps(actions);
1843 } else {
1844 QString error = tr("Error loading map:") + "\n"
1845 + Util::displayName(map->path()) + ": " + map->errorString();
1846 QMessageBox::critical(this, APP_NAME, error);
1847 action->deleteLater();
1851 void GUI::loadMapDirNode(const TreeNode<Map *> &node, QList<MapAction*> &actions,
1852 QMenu *menu, const QList<QAction*> &existingActions, int &showError)
1854 for (int i = 0; i < node.childs().size(); i++) {
1855 QMenu *cm = new QMenu(node.childs().at(i).name(), menu);
1856 menu->addMenu(cm);
1857 loadMapDirNode(node.childs().at(i), actions, cm, existingActions,
1858 showError);
1861 for (int i = 0; i < node.items().size(); i++) {
1862 Map *map = node.items().at(i);
1863 MapAction *a;
1865 if (!(a = findMapAction(existingActions, map))) {
1866 if (!map->isValid()) {
1867 if (showError) {
1868 QString error = tr("Error loading map:") + "\n"
1869 + Util::displayName(map->path()) + ": " + map->errorString();
1871 if (showError > 1) {
1872 QMessageBox message(QMessageBox::Critical, APP_NAME,
1873 error, QMessageBox::Ok, this);
1874 QCheckBox checkBox(tr("Don't show again"));
1875 message.setCheckBox(&checkBox);
1876 message.exec();
1877 if (checkBox.isChecked())
1878 showError = 0;
1879 } else
1880 QMessageBox::critical(this, APP_NAME, error);
1883 delete map;
1884 } else {
1885 a = new MapAction(map, _mapsActionGroup);
1886 menu->addAction(a);
1888 if (map->isReady()) {
1889 _showMapAction->setEnabled(true);
1890 _clearMapCacheAction->setEnabled(true);
1891 actions.append(a);
1892 } else
1893 connect(a, &MapAction::loaded, this, &GUI::mapLoadedDir);
1895 _areaCount++;
1897 } else {
1898 map = a->data().value<Map*>();
1899 if (map->isReady())
1900 actions.append(a);
1905 void GUI::loadMapDir()
1907 QString dir(QFileDialog::getExistingDirectory(this,
1908 tr("Select map directory"), _mapDir, QFileDialog::ShowDirsOnly));
1909 if (dir.isEmpty())
1910 return;
1912 QFileInfo fi(dir);
1913 TreeNode<Map*> maps(MapList::loadMaps(dir, _mapView->inputProjection()));
1914 QList<QAction*> existingActions(_mapsActionGroup->actions());
1915 QList<MapAction*> actions;
1916 QMenu *menu = new QMenu(maps.name());
1917 int showError = (maps.items().size() > 1 || !maps.childs().isEmpty())
1918 ? 2 : 1;
1920 loadMapDirNode(maps, actions, menu, existingActions, showError);
1922 _mapView->loadMaps(actions);
1924 if (menu->isEmpty())
1925 delete menu;
1926 else
1927 _mapMenu->insertMenu(_mapsEnd, menu);
1929 _mapDir = fi.absolutePath();
1930 _fileActionGroup->setEnabled(true);
1931 _reloadFileAction->setEnabled(false);
1934 void GUI::clearMapCache()
1936 if (QMessageBox::question(this, APP_NAME,
1937 tr("Clear \"%1\" tile cache?").arg(_map->name())) == QMessageBox::Yes)
1938 _mapView->clearMapCache();
1941 void GUI::downloadDEM()
1943 RectC br(_mapView->boundingRect());
1944 _demRects.append(br);
1946 if (!_dem->loadTiles(br) && _demRects.size() == 1)
1947 demLoaded();
1950 void GUI::demLoaded()
1952 for (int i = 0; i < _demRects.size(); i++) {
1953 if (!_dem->checkTiles(_demRects.at(i))) {
1954 QMessageBox::warning(this, APP_NAME,
1955 tr("Could not download all required DEM files."));
1956 break;
1960 DEM::clearCache();
1961 _demRects.clear();
1962 reloadFiles();
1965 void GUI::showDEMTiles()
1967 QList<Area> tiles(DEM::tiles());
1969 if (tiles.isEmpty()) {
1970 QMessageBox::information(this, APP_NAME, tr("No local DEM tiles found."));
1971 } else {
1972 _mapView->loadDEMs(tiles);
1974 _areaCount += tiles.size();
1976 _fileActionGroup->setEnabled(true);
1977 _reloadFileAction->setEnabled(false);
1981 void GUI::updateStatusBarInfo()
1983 if (_files.count() == 0)
1984 _fileNameLabel->setText(tr("No files loaded"));
1985 else if (_files.count() == 1)
1986 _fileNameLabel->setText(Util::displayName(_files.at(0)));
1987 else
1988 _fileNameLabel->setText(tr("%n files", "", _files.count()));
1990 if (distance() > 0)
1991 _distanceLabel->setText(Format::distance(distance(), units()));
1992 else
1993 _distanceLabel->clear();
1995 if (time() > 0) {
1996 if (_movingTimeAction->isChecked()) {
1997 _timeLabel->setText(Format::timeSpan(movingTime())
1998 + "<sub>M</sub>");
1999 _timeLabel->setToolTip(Format::timeSpan(time()));
2000 } else {
2001 _timeLabel->setText(Format::timeSpan(time()));
2002 _timeLabel->setToolTip(Format::timeSpan(movingTime())
2003 + "<sub>M</sub>");
2005 } else {
2006 _timeLabel->clear();
2007 _timeLabel->setToolTip(QString());
2010 #ifdef Q_OS_ANDROID
2011 statusBar()->setVisible(!_files.isEmpty());
2012 #endif // Q_OS_ANDROID
2015 void GUI::updateWindowTitle()
2017 if (_files.count() == 1)
2018 setWindowTitle(QFileInfo(_files.at(0)).fileName() + " - " + APP_NAME);
2019 else
2020 setWindowTitle(APP_NAME);
2023 #ifndef Q_OS_ANDROID
2024 void GUI::updateRecentFiles(const QString &fileName)
2026 QAction *a = 0;
2028 QList<QAction *> actions(_recentFilesActionGroup->actions());
2029 for (int i = 0; i < actions.size(); i++) {
2030 if (actions.at(i)->text() == fileName) {
2031 a = actions.at(i);
2032 break;
2036 if (a)
2037 delete a;
2038 else if (actions.size() == MAX_RECENT_FILES)
2039 delete actions.last();
2041 actions = _recentFilesActionGroup->actions();
2042 QAction *before = actions.size() ? actions.last() : _recentFilesEnd;
2043 _recentFilesMenu->insertAction(before,
2044 new QAction(fileName, _recentFilesActionGroup));
2045 _recentFilesMenu->setEnabled(true);
2048 void GUI::clearRecentFiles()
2050 QList<QAction *> actions(_recentFilesActionGroup->actions());
2052 for (int i = 0; i < actions.size(); i++)
2053 delete actions.at(i);
2055 _recentFilesMenu->setEnabled(false);
2058 void GUI::recentFileSelected(QAction *action)
2060 int showError = 1;
2061 openFile(action->text(), true, showError);
2063 #endif // Q_OS_ANDROID
2065 void GUI::mapChanged(QAction *action)
2067 _map = action->data().value<Map*>();
2068 _mapView->setMap(_map);
2071 void GUI::nextMap()
2073 QAction *checked = _mapsActionGroup->checkedAction();
2074 if (!checked)
2075 return;
2077 QList<QAction*> maps(_mapsActionGroup->actions());
2078 for (int i = 1; i < maps.size(); i++) {
2079 int next = (maps.indexOf(checked) + i) % maps.count();
2080 if (maps.at(next)->isEnabled()) {
2081 maps.at(next)->trigger();
2082 break;
2087 void GUI::prevMap()
2089 QAction *checked = _mapsActionGroup->checkedAction();
2090 if (!checked)
2091 return;
2093 QList<QAction*> maps(_mapsActionGroup->actions());
2094 for (int i = 1; i < maps.size(); i++) {
2095 int prev = (maps.indexOf(checked) + maps.count() - i) % maps.count();
2096 if (maps.at(prev)->isEnabled()) {
2097 maps.at(prev)->trigger();
2098 break;
2103 void GUI::poiFileChecked(QAction *action)
2105 _poi->enableFile(action->data().value<QString>(), action->isChecked());
2108 void GUI::selectAllPOIs()
2110 QList<QAction*> actions(_poisActionGroup->actions());
2111 for (int i = 0; i < actions.size(); i++) {
2112 POIAction *a = static_cast<POIAction*>(actions.at(i));
2113 if (_poi->enableFile(a->data().toString(), true))
2114 a->setChecked(true);
2118 void GUI::unselectAllPOIs()
2120 QList<QAction*> actions(_poisActionGroup->actions());
2121 for (int i = 0; i < actions.size(); i++) {
2122 POIAction *a = static_cast<POIAction*>(actions.at(i));
2123 if (_poi->enableFile(a->data().toString(), false))
2124 a->setChecked(false);
2128 void GUI::graphChanged(int index)
2130 if (index < 0)
2131 return;
2133 GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->widget(index));
2135 _mapView->setGraph(_tabs.indexOf(gt));
2137 if (_lastTab)
2138 gt->setSliderPosition(_lastTab->sliderPosition());
2139 _lastTab = gt;
2142 void GUI::updateNavigationActions()
2144 #ifdef Q_OS_ANDROID
2145 _navigation->enableNext(!_browser->isLast()
2146 && !_browser->current().isNull());
2147 _navigation->enablePrev(!_browser->isFirst()
2148 && !_browser->current().isNull());
2149 #else // Q_OS_ANDROID
2150 _lastAction->setEnabled(!_browser->isLast());
2151 _nextAction->setEnabled(!_browser->isLast());
2152 _firstAction->setEnabled(!_browser->isFirst());
2153 _prevAction->setEnabled(!_browser->isFirst());
2154 #endif // Q_OS_ANDROID
2157 bool GUI::updateGraphTabs()
2159 int index;
2160 GraphTab *tab;
2161 bool hidden = _graphTabWidget->isHidden();
2163 for (int i = 0; i < _tabs.size(); i++) {
2164 tab = _tabs.at(i);
2165 if (tab->isEmpty() && (index = _graphTabWidget->indexOf(tab)) >= 0)
2166 _graphTabWidget->removeTab(index);
2169 for (int i = 0; i < _tabs.size(); i++) {
2170 tab = _tabs.at(i);
2171 if (!tab->isEmpty() && _graphTabWidget->indexOf(tab) < 0)
2172 _graphTabWidget->insertTab(i, tab, _tabs.at(i)->label());
2175 if (_graphTabWidget->count() &&
2176 ((_showTracksAction->isChecked() && _trackCount)
2177 || (_showRoutesAction->isChecked() && _routeCount))) {
2178 if (_showGraphsAction->isChecked())
2179 _graphTabWidget->setHidden(false);
2180 _showGraphsAction->setEnabled(true);
2181 } else {
2182 _graphTabWidget->setHidden(true);
2183 _showGraphsAction->setEnabled(false);
2186 return (hidden != _graphTabWidget->isHidden());
2189 void GUI::updateDEMDownloadAction()
2191 _downloadDEMAction->setEnabled(!_dem->url().isEmpty()
2192 && !_dem->checkTiles(_mapView->boundingRect()));
2195 void GUI::setTimeType(TimeType type)
2197 for (int i = 0; i <_tabs.count(); i++)
2198 _tabs.at(i)->setTimeType(type);
2200 updateStatusBarInfo();
2203 void GUI::setUnits(Units units)
2205 _units = units;
2207 _mapView->setUnits(units);
2208 for (int i = 0; i <_tabs.count(); i++)
2209 _tabs.at(i)->setUnits(units);
2210 updateStatusBarInfo();
2213 void GUI::setCoordinatesFormat(CoordinatesFormat format)
2215 _mapView->setCoordinatesFormat(format);
2218 void GUI::setGraphType(GraphType type)
2220 for (int i = 0; i <_tabs.count(); i++)
2221 _tabs.at(i)->setGraphType(type);
2224 void GUI::next()
2226 int showError = 1;
2227 QString file = _browser->next();
2228 if (file.isNull())
2229 return;
2231 closeFiles();
2232 openFile(file, true, showError);
2235 void GUI::prev()
2237 int showError = 1;
2238 QString file = _browser->prev();
2239 if (file.isNull())
2240 return;
2242 closeFiles();
2243 openFile(file, true, showError);
2246 void GUI::last()
2248 int showError = 1;
2249 QString file = _browser->last();
2250 if (file.isNull())
2251 return;
2253 closeFiles();
2254 openFile(file, true, showError);
2257 void GUI::first()
2259 int showError = 1;
2260 QString file = _browser->first();
2261 if (file.isNull())
2262 return;
2264 closeFiles();
2265 openFile(file, true, showError);
2268 #ifndef Q_OS_ANDROID
2269 void GUI::keyPressEvent(QKeyEvent *event)
2271 QString file;
2272 int showError = 1;
2274 switch (event->key()) {
2275 case PREV_KEY:
2276 file = _browser->prev();
2277 break;
2278 case NEXT_KEY:
2279 file = _browser->next();
2280 break;
2281 case FIRST_KEY:
2282 file = _browser->first();
2283 break;
2284 case LAST_KEY:
2285 file = _browser->last();
2286 break;
2288 case TOGGLE_GRAPH_TYPE_KEY:
2289 if (_timeGraphAction->isChecked())
2290 _distanceGraphAction->trigger();
2291 else
2292 _timeGraphAction->trigger();
2293 break;
2294 case TOGGLE_TIME_TYPE_KEY:
2295 if (_movingTimeAction->isChecked())
2296 _totalTimeAction->trigger();
2297 else
2298 _movingTimeAction->trigger();
2299 break;
2300 case TOGGLE_MARKER_INFO_KEY:
2301 if (_showMarkerDateAction->isChecked())
2302 _showMarkerCoordinatesAction->trigger();
2303 else if (_showMarkerCoordinatesAction->isChecked())
2304 _showMarkerDateAction->trigger();
2305 break;
2306 case Qt::Key_Escape:
2307 if (_fullscreenAction->isChecked()) {
2308 _fullscreenAction->setChecked(false);
2309 showFullscreen(false);
2310 return;
2312 break;
2315 if (!file.isNull()) {
2316 if (!(event->modifiers() & MODIFIER))
2317 closeFiles();
2318 openFile(file, true, showError);
2319 return;
2322 QMainWindow::keyPressEvent(event);
2324 #endif // Q_OS_ANDROID
2326 void GUI::closeEvent(QCloseEvent *event)
2328 writeSettings();
2329 QMainWindow::closeEvent(event);
2332 void GUI::dragEnterEvent(QDragEnterEvent *event)
2334 if (!event->mimeData()->hasUrls())
2335 return;
2336 if (event->proposedAction() != Qt::CopyAction)
2337 return;
2339 QList<QUrl> urls = event->mimeData()->urls();
2340 for (int i = 0; i < urls.size(); i++)
2341 if (!urls.at(i).isLocalFile())
2342 return;
2344 event->acceptProposedAction();
2347 void GUI::dropEvent(QDropEvent *event)
2349 MapAction *lastReady = 0;
2350 QList<QUrl> urls(event->mimeData()->urls());
2351 int silent = 0;
2352 int showError = (urls.size() > 1) ? 2 : 1;
2354 for (int i = 0; i < urls.size(); i++) {
2355 QString file(urls.at(i).toLocalFile());
2357 if (!openFile(file, false, silent)) {
2358 MapAction *a;
2359 if (!loadMap(file, a, silent))
2360 openFile(file, true, showError);
2361 else {
2362 if (a)
2363 lastReady = a;
2368 if (lastReady)
2369 lastReady->trigger();
2371 event->acceptProposedAction();
2374 QGeoPositionInfoSource *GUI::positionSource(const Options &options)
2376 QGeoPositionInfoSource *source;
2378 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
2379 source = QGeoPositionInfoSource::createSource(options.plugin, this);
2380 #else // QT 5.14
2381 source = QGeoPositionInfoSource::createSource(options.plugin,
2382 options.pluginParams.value(options.plugin), this);
2383 #endif // QT 5.14
2384 if (source)
2385 source->setPreferredPositioningMethods(
2386 QGeoPositionInfoSource::SatellitePositioningMethods);
2388 return source;
2391 void GUI::writeSettings()
2393 #define WRITE(name, value) \
2394 Settings::name.write(settings, value);
2396 QSettings settings(qApp->applicationName(), qApp->applicationName());
2397 settings.clear();
2399 /* Window */
2400 #ifndef Q_OS_ANDROID
2401 settings.beginGroup(SETTINGS_WINDOW);
2402 if (!_windowStates.isEmpty() && !_windowGeometries.isEmpty()) {
2403 WRITE(windowState, _windowStates.first());
2404 WRITE(windowGeometry, _windowGeometries.first());
2405 } else {
2406 WRITE(windowState, saveState());
2407 WRITE(windowGeometry, saveGeometry());
2409 settings.endGroup();
2410 #endif // Q_OS_ANDROID
2412 /* Settings */
2413 settings.beginGroup(SETTINGS_SETTINGS);
2414 WRITE(timeType, _movingTimeAction->isChecked() ? Moving : Total);
2415 WRITE(units, _imperialUnitsAction->isChecked()
2416 ? Imperial : _nauticalUnitsAction->isChecked()
2417 ? Nautical : Metric);
2418 WRITE(coordinatesFormat, _dmsAction->isChecked()
2419 ? DMS : _degreesMinutesAction->isChecked()
2420 ? DegreesMinutes : DecimalDegrees);
2421 #ifndef Q_OS_ANDROID
2422 WRITE(showToolbars, _showToolbarsAction->isChecked());
2423 #endif // Q_OS_ANDROID
2424 settings.endGroup();
2426 /* File */
2427 #ifndef Q_OS_ANDROID
2428 QList<QAction*> recentActions(_recentFilesActionGroup->actions());
2429 QStringList recent;
2430 for (int i = 0; i < recentActions.size(); i++)
2431 recent.append(recentActions.at(i)->text());
2433 settings.beginGroup(SETTINGS_FILE);
2434 WRITE(recentDataFiles, recent);
2435 settings.endGroup();
2436 #endif // Q_OS_ANDROID
2438 /* Map */
2439 settings.beginGroup(SETTINGS_MAP);
2440 WRITE(activeMap, _map->name());
2441 WRITE(showMap, _showMapAction->isChecked());
2442 WRITE(cursorCoordinates, _showCoordinatesAction->isChecked());
2443 settings.endGroup();
2445 /* Graph */
2446 settings.beginGroup(SETTINGS_GRAPH);
2447 WRITE(showGraphs, _showGraphsAction->isChecked());
2448 WRITE(graphType, _timeGraphAction->isChecked() ? Time : Distance);
2449 WRITE(showGrid, _showGraphGridAction->isChecked());
2450 WRITE(sliderInfo, _showGraphSliderInfoAction->isChecked());
2451 #ifdef Q_OS_ANDROID
2452 WRITE(showGraphTabs, _showGraphTabsAction->isChecked());
2453 #endif // Q_OS_ANDROID
2454 settings.endGroup();
2456 /* POI */
2457 QList<QAction*> disabledActions(_poisActionGroup->actions());
2458 QStringList disabled;
2459 for (int i = 0; i < disabledActions.size(); i++)
2460 if (!disabledActions.at(i)->isChecked())
2461 disabled.append(disabledActions.at(i)->data().toString());
2463 settings.beginGroup(SETTINGS_POI);
2464 WRITE(showPoi, _showPOIAction->isChecked());
2465 WRITE(poiOverlap, _overlapPOIAction->isChecked());
2466 WRITE(poiLabels, _showPOILabelsAction->isChecked());
2467 WRITE(poiIcons, _showPOIIconsAction->isChecked());
2468 WRITE(disabledPoiFiles, disabled);
2469 settings.endGroup();
2471 /* Data */
2472 MarkerInfoItem::Type mi;
2473 if (_showMarkerDateAction->isChecked())
2474 mi = MarkerInfoItem::Date;
2475 else if (_showMarkerCoordinatesAction->isChecked())
2476 mi = MarkerInfoItem::Position;
2477 else
2478 mi = MarkerInfoItem::None;
2480 settings.beginGroup(SETTINGS_DATA);
2481 WRITE(tracks, _showTracksAction->isChecked());
2482 WRITE(routes, _showRoutesAction->isChecked());
2483 WRITE(waypoints, _showWaypointsAction->isChecked());
2484 WRITE(areas, _showAreasAction->isChecked());
2485 WRITE(waypointIcons, _showWaypointIconsAction->isChecked());
2486 WRITE(waypointLabels, _showWaypointLabelsAction->isChecked());
2487 WRITE(routeWaypoints, _showRouteWaypointsAction->isChecked());
2488 WRITE(pathTicks, _showTicksAction->isChecked());
2489 WRITE(positionMarkers, _showMarkersAction->isChecked()
2490 || _showMarkerDateAction->isChecked()
2491 || _showMarkerCoordinatesAction->isChecked());
2492 WRITE(markerInfo, mi);
2493 WRITE(useStyles, _useStylesAction->isChecked());
2494 settings.endGroup();
2496 /* Position */
2497 settings.beginGroup(SETTINGS_POSITION);
2498 WRITE(showPosition, _showPositionAction->isChecked());
2499 WRITE(followPosition, _followPositionAction->isChecked());
2500 WRITE(positionCoordinates, _showPositionCoordinatesAction->isChecked());
2501 WRITE(motionInfo, _showMotionInfoAction->isChecked());
2502 settings.endGroup();
2504 /* PDF export */
2505 settings.beginGroup(SETTINGS_PDF_EXPORT);
2506 WRITE(pdfOrientation, _pdfExport.orientation);
2507 WRITE(pdfResolution, _pdfExport.resolution);
2508 WRITE(pdfSize, _pdfExport.paperSize);
2509 WRITE(pdfMarginLeft, _pdfExport.margins.left());
2510 WRITE(pdfMarginTop, _pdfExport.margins.top());
2511 WRITE(pdfMarginRight, _pdfExport.margins.right());
2512 WRITE(pdfMarginBottom, _pdfExport.margins.bottom());
2513 WRITE(pdfFileName, _pdfExport.fileName);
2514 settings.endGroup();
2516 /* PNG export */
2517 settings.beginGroup(SETTINGS_PNG_EXPORT);
2518 WRITE(pngWidth, _pngExport.size.width());
2519 WRITE(pngHeight, _pngExport.size.height());
2520 WRITE(pngMarginLeft, _pngExport.margins.left());
2521 WRITE(pngMarginTop, _pngExport.margins.top());
2522 WRITE(pngMarginRight, _pngExport.margins.right());
2523 WRITE(pngMarginBottom, _pngExport.margins.bottom());
2524 WRITE(pngAntialiasing, _pngExport.antialiasing);
2525 WRITE(pngFileName, _pngExport.fileName);
2526 settings.endGroup();
2528 /* Options */
2529 settings.beginGroup(SETTINGS_OPTIONS);
2530 WRITE(paletteColor, _options.palette.color());
2531 WRITE(paletteShift, _options.palette.shift());
2532 WRITE(mapOpacity, _options.mapOpacity);
2533 WRITE(backgroundColor, _options.backgroundColor);
2534 WRITE(crosshairColor, _options.crosshairColor);
2535 WRITE(infoColor, _options.infoColor);
2536 WRITE(infoBackground, _options.infoBackground);
2537 WRITE(trackWidth, _options.trackWidth);
2538 WRITE(routeWidth, _options.routeWidth);
2539 WRITE(areaWidth, _options.areaWidth);
2540 WRITE(trackStyle, (int)_options.trackStyle);
2541 WRITE(routeStyle, (int)_options.routeStyle);
2542 WRITE(areaStyle, (int)_options.areaStyle);
2543 WRITE(areaOpacity, _options.areaOpacity);
2544 WRITE(waypointSize, _options.waypointSize);
2545 WRITE(waypointColor, _options.waypointColor);
2546 WRITE(poiSize, _options.poiSize);
2547 WRITE(poiColor, _options.poiColor);
2548 WRITE(graphWidth, _options.graphWidth);
2549 WRITE(pathAntiAliasing, _options.pathAntiAliasing);
2550 WRITE(graphAntiAliasing, _options.graphAntiAliasing);
2551 WRITE(elevationFilter, _options.elevationFilter);
2552 WRITE(speedFilter, _options.speedFilter);
2553 WRITE(heartRateFilter, _options.heartRateFilter);
2554 WRITE(cadenceFilter, _options.cadenceFilter);
2555 WRITE(powerFilter, _options.powerFilter);
2556 WRITE(outlierEliminate, _options.outlierEliminate);
2557 WRITE(automaticPause, _options.automaticPause);
2558 WRITE(pauseSpeed, _options.pauseSpeed);
2559 WRITE(pauseInterval, _options.pauseInterval);
2560 WRITE(useReportedSpeed, _options.useReportedSpeed);
2561 WRITE(dataUseDEM, _options.dataUseDEM);
2562 WRITE(secondaryElevation, _options.showSecondaryElevation);
2563 WRITE(secondarySpeed, _options.showSecondarySpeed);
2564 WRITE(timeZone, QVariant::fromValue(_options.timeZone));
2565 WRITE(useSegments, _options.useSegments);
2566 WRITE(poiRadius, _options.poiRadius);
2567 WRITE(demURL, _options.demURL);
2568 WRITE(demAuthentication, _options.demAuthorization);
2569 WRITE(demUsername, _options.demUsername);
2570 WRITE(demPassword, _options.demPassword);
2571 WRITE(positionPlugin(), _options.plugin);
2572 WRITE(positionPluginParameters, _options.pluginParams);
2573 WRITE(useOpenGL, _options.useOpenGL);
2574 WRITE(enableHTTP2, _options.enableHTTP2);
2575 WRITE(pixmapCache, _options.pixmapCache);
2576 WRITE(demCache, _options.demCache);
2577 WRITE(connectionTimeout, _options.connectionTimeout);
2578 WRITE(hiresPrint, _options.hiresPrint);
2579 WRITE(printName, _options.printName);
2580 WRITE(printDate, _options.printDate);
2581 WRITE(printDistance, _options.printDistance);
2582 WRITE(printTime, _options.printTime);
2583 WRITE(printMovingTime, _options.printMovingTime);
2584 WRITE(printItemCount, _options.printItemCount);
2585 WRITE(separateGraphPage, _options.separateGraphPage);
2586 WRITE(sliderColor, _options.sliderColor);
2587 WRITE(outputProjection, _options.outputProjection);
2588 WRITE(inputProjection, _options.inputProjection);
2589 WRITE(hidpiMap, _options.hidpiMap);
2590 WRITE(dataPath, _options.dataPath);
2591 WRITE(mapsPath, _options.mapsPath);
2592 WRITE(poiPath, _options.poiPath);
2593 settings.endGroup();
2596 void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs,
2597 QStringList &recentFiles)
2599 #define READ(name) \
2600 (Settings::name.read(settings))
2602 QSettings settings(qApp->applicationName(), qApp->applicationName());
2604 #ifndef Q_OS_ANDROID
2605 settings.beginGroup(SETTINGS_WINDOW);
2606 restoreGeometry(READ(windowGeometry).toByteArray());
2607 restoreState(READ(windowState).toByteArray());
2608 settings.endGroup();
2609 #endif // Q_OS_ANDROID
2611 /* Settings */
2612 settings.beginGroup(SETTINGS_SETTINGS);
2613 TimeType tt = (TimeType)READ(timeType).toInt();
2614 if (tt == Moving)
2615 _movingTimeAction->setChecked(true);
2616 else
2617 _totalTimeAction->setChecked(true);
2618 setTimeType(tt);
2620 Units u = (Units)READ(units).toInt();
2621 if (u == Imperial)
2622 _imperialUnitsAction->setChecked(true);
2623 else if (u == Nautical)
2624 _nauticalUnitsAction->setChecked(true);
2625 else
2626 _metricUnitsAction->setChecked(true);
2627 setUnits(u);
2629 CoordinatesFormat cf = (CoordinatesFormat)READ(coordinatesFormat).toInt();
2630 if (cf == DMS)
2631 _dmsAction->setChecked(true);
2632 else if (cf == DegreesMinutes)
2633 _degreesMinutesAction->setChecked(true);
2634 else
2635 _decimalDegreesAction->setChecked(true);
2636 setCoordinatesFormat(cf);
2638 #ifndef Q_OS_ANDROID
2639 if (READ(showToolbars).toBool())
2640 _showToolbarsAction->setChecked(true);
2641 else
2642 showToolbars(false);
2643 #endif // Q_OS_ANDROID
2644 settings.endGroup();
2646 /* File */
2647 #ifndef Q_OS_ANDROID
2648 settings.beginGroup(SETTINGS_FILE);
2649 recentFiles = READ(recentDataFiles);
2650 settings.endGroup();
2651 #else // Q_OS_ANDROID
2652 Q_UNUSED(recentFiles);
2653 #endif // Q_OS_ANDROID
2655 /* Map */
2656 settings.beginGroup(SETTINGS_MAP);
2657 if (READ(showMap).toBool()) {
2658 _showMapAction->setChecked(true);
2659 _mapView->showMap(true);
2661 if (READ(cursorCoordinates).toBool()) {
2662 _showCoordinatesAction->setChecked(true);
2663 _mapView->showCursorCoordinates(true);
2665 activeMap = READ(activeMap).toString();
2666 settings.endGroup();
2668 /* Graph */
2669 settings.beginGroup(SETTINGS_GRAPH);
2670 if (READ(showGraphs).toBool())
2671 _showGraphsAction->setChecked(true);
2672 else
2673 showGraphs(false);
2675 GraphType gt = (GraphType)READ(graphType).toInt();
2676 if (gt == Time)
2677 _timeGraphAction->setChecked(true);
2678 else
2679 _distanceGraphAction->setChecked(true);
2680 setGraphType(gt);
2682 if (READ(showGrid).toBool())
2683 _showGraphGridAction->setChecked(true);
2684 else
2685 showGraphGrids(false);
2687 if (READ(sliderInfo).toBool())
2688 _showGraphSliderInfoAction->setChecked(true);
2689 else
2690 showGraphSliderInfo(false);
2692 #ifdef Q_OS_ANDROID
2693 if (READ(showGraphTabs).toBool())
2694 _showGraphTabsAction->setChecked(true);
2695 else
2696 showGraphTabs(false);
2697 #endif // Q_OS_ANDROID
2698 settings.endGroup();
2700 /* POI */
2701 settings.beginGroup(SETTINGS_POI);
2702 if (READ(poiOverlap).toBool()) {
2703 _overlapPOIAction->setChecked(true);
2704 _mapView->showOverlappedPOIs(true);
2706 if (READ(poiIcons).toBool()) {
2707 _showPOIIconsAction->setChecked(true);
2708 _mapView->showPOIIcons(true);
2710 if (READ(poiLabels).toBool()) {
2711 _showPOILabelsAction->setChecked(true);
2712 _mapView->showPOILabels(true);
2714 if (READ(showPoi).toBool()) {
2715 _showPOIAction->setChecked(true);
2716 _mapView->showPOI(true);
2718 disabledPOIs = READ(disabledPoiFiles);
2719 settings.endGroup();
2721 /* Data */
2722 settings.beginGroup(SETTINGS_DATA);
2723 if (READ(tracks).toBool()) {
2724 _showTracksAction->setChecked(true);
2725 _mapView->showTracks(true);
2726 for (int i = 0; i < _tabs.count(); i++)
2727 _tabs.at(i)->showTracks(true);
2729 if (READ(routes).toBool()) {
2730 _showRoutesAction->setChecked(true);
2731 _mapView->showRoutes(true);
2732 for (int i = 0; i < _tabs.count(); i++)
2733 _tabs.at(i)->showRoutes(true);
2735 if (READ(waypoints).toBool()) {
2736 _showWaypointsAction->setChecked(true);
2737 _mapView->showWaypoints(true);
2739 if (READ(areas).toBool()) {
2740 _showAreasAction->setChecked(true);
2741 _mapView->showAreas(true);
2743 if (READ(waypointIcons).toBool()) {
2744 _showWaypointIconsAction->setChecked(true);
2745 _mapView->showWaypointIcons(true);
2747 if (READ(waypointLabels).toBool()) {
2748 _showWaypointLabelsAction->setChecked(true);
2749 _mapView->showWaypointLabels(true);
2751 if (READ(routeWaypoints).toBool()) {
2752 _showRouteWaypointsAction->setChecked(true);
2753 _mapView->showRouteWaypoints(true);
2755 if (READ(pathTicks).toBool()) {
2756 _showTicksAction->setChecked(true);
2757 _mapView->showTicks(true);
2759 if (READ(useStyles).toBool()) {
2760 _useStylesAction->setChecked(true);
2761 _mapView->useStyles(true);
2763 if (READ(positionMarkers).toBool()) {
2764 MarkerInfoItem::Type mt = (MarkerInfoItem::Type)READ(markerInfo).toInt();
2765 if (mt == MarkerInfoItem::Position)
2766 _showMarkerCoordinatesAction->setChecked(true);
2767 else if (mt == MarkerInfoItem::Date)
2768 _showMarkerDateAction->setChecked(true);
2769 else
2770 _showMarkersAction->setChecked(true);
2772 _mapView->showMarkers(true);
2773 _mapView->showMarkerInfo(mt);
2774 } else
2775 _hideMarkersAction->setChecked(true);
2776 settings.endGroup();
2778 /* Position */
2779 settings.beginGroup(SETTINGS_POSITION);
2780 if (READ(showPosition).toBool()) {
2781 _showPositionAction->setChecked(true);
2782 _mapView->showPosition(true);
2784 if (READ(followPosition).toBool()) {
2785 _followPositionAction->setChecked(true);
2786 _mapView->followPosition(true);
2788 if (READ(positionCoordinates).toBool()) {
2789 _showPositionCoordinatesAction->setChecked(true);
2790 _mapView->showPositionCoordinates(true);
2792 if (READ(motionInfo).toBool()) {
2793 _showMotionInfoAction->setChecked(true);
2794 _mapView->showMotionInfo(true);
2796 settings.endGroup();
2798 /* PDF export */
2799 settings.beginGroup(SETTINGS_PDF_EXPORT);
2800 _pdfExport.orientation = (QPageLayout::Orientation)READ(pdfOrientation)
2801 .toInt();
2802 _pdfExport.resolution = READ(pdfResolution).toInt();
2803 _pdfExport.paperSize = (QPageSize::PageSizeId)READ(pdfSize).toInt();
2804 _pdfExport.margins = QMarginsF(READ(pdfMarginLeft).toReal(),
2805 READ(pdfMarginTop).toReal(), READ(pdfMarginRight).toReal(),
2806 READ(pdfMarginBottom).toReal());
2807 _pdfExport.fileName = READ(pdfFileName).toString();
2808 settings.endGroup();
2810 /* PNG export */
2811 settings.beginGroup(SETTINGS_PNG_EXPORT);
2812 _pngExport.size = QSize(READ(pngWidth).toInt(), READ(pngHeight).toInt());
2813 _pngExport.margins = QMargins(READ(pngMarginLeft).toInt(),
2814 READ(pngMarginTop).toInt(), READ(pngMarginRight).toInt(),
2815 READ(pngMarginBottom).toInt());
2816 _pngExport.antialiasing = READ(pngAntialiasing).toBool();
2817 _pngExport.fileName = READ(pngFileName).toString();
2818 settings.endGroup();
2820 /* Options */
2821 settings.beginGroup(SETTINGS_OPTIONS);
2822 _options.palette = Palette(READ(paletteColor).value<QColor>(),
2823 READ(paletteShift).toDouble());
2824 _options.mapOpacity = READ(mapOpacity).toInt();
2825 _options.backgroundColor = READ(backgroundColor).value<QColor>();
2826 _options.crosshairColor = READ(crosshairColor).value<QColor>();
2827 _options.infoColor = READ(infoColor).value<QColor>();
2828 _options.infoBackground = READ(infoBackground).toBool();
2829 _options.trackWidth = READ(trackWidth).toInt();
2830 _options.routeWidth = READ(routeWidth).toInt();
2831 _options.areaWidth = READ(areaWidth).toInt();
2832 _options.trackStyle = (Qt::PenStyle)READ(trackStyle).toInt();
2833 _options.routeStyle = (Qt::PenStyle)READ(routeStyle).toInt();
2834 _options.areaStyle = (Qt::PenStyle)READ(areaStyle).toInt();
2835 _options.areaOpacity = READ(areaOpacity).toInt();
2836 _options.pathAntiAliasing = READ(pathAntiAliasing).toBool();
2837 _options.waypointSize = READ(waypointSize).toInt();
2838 _options.waypointColor = READ(waypointColor).value<QColor>();
2839 _options.poiSize = READ(poiSize).toInt();
2840 _options.poiColor = READ(poiColor).value<QColor>();
2841 _options.graphWidth = READ(graphWidth).toInt();
2842 _options.graphAntiAliasing = READ(graphAntiAliasing).toBool();
2843 _options.elevationFilter = READ(elevationFilter).toInt();
2844 _options.speedFilter = READ(speedFilter).toInt();
2845 _options.heartRateFilter = READ(heartRateFilter).toInt();
2846 _options.cadenceFilter = READ(cadenceFilter).toInt();
2847 _options.powerFilter = READ(powerFilter).toInt();
2848 _options.outlierEliminate = READ(outlierEliminate).toBool();
2849 _options.pauseSpeed = READ(pauseSpeed).toFloat();
2850 _options.automaticPause = READ(automaticPause).toBool();
2851 _options.pauseInterval = READ(pauseInterval).toInt();
2852 _options.useReportedSpeed = READ(useReportedSpeed).toBool();
2853 _options.dataUseDEM = READ(dataUseDEM).toBool();
2854 _options.showSecondaryElevation = READ(secondaryElevation).toBool();
2855 _options.showSecondarySpeed = READ(secondarySpeed).toBool();
2856 _options.timeZone = READ(timeZone).value<TimeZoneInfo>();
2857 _options.useSegments = READ(useSegments).toBool();
2858 _options.poiRadius = READ(poiRadius).toInt();
2859 _options.demURL = READ(demURL).toString();
2860 _options.demAuthorization = READ(demAuthentication).toBool();
2861 _options.demUsername = READ(demUsername).toString();
2862 _options.demPassword = READ(demPassword).toString();
2863 _options.plugin = READ(positionPlugin()).toString();
2864 _options.pluginParams = READ(positionPluginParameters);
2865 _options.useOpenGL = READ(useOpenGL).toBool();
2866 _options.enableHTTP2 = READ(enableHTTP2).toBool();
2867 _options.pixmapCache = READ(pixmapCache).toInt();
2868 _options.demCache = READ(demCache).toInt();
2869 _options.connectionTimeout = READ(connectionTimeout).toInt();
2870 _options.hiresPrint = READ(hiresPrint).toBool();
2871 _options.printName = READ(printName).toBool();
2872 _options.printDate = READ(printDate).toBool();
2873 _options.printDistance = READ(printDistance).toBool();
2874 _options.printTime = READ(printTime).toBool();
2875 _options.printMovingTime = READ(printMovingTime).toBool();
2876 _options.printItemCount = READ(printItemCount).toBool();
2877 _options.separateGraphPage = READ(separateGraphPage).toBool();
2878 _options.sliderColor = READ(sliderColor).value<QColor>();
2879 _options.outputProjection = READ(outputProjection).toInt();
2880 _options.inputProjection = READ(inputProjection).toInt();
2881 _options.hidpiMap = READ(hidpiMap).toBool();
2882 _options.dataPath = READ(dataPath).toString();
2883 _options.mapsPath = READ(mapsPath).toString();
2884 _options.poiPath = READ(poiPath).toString();
2885 settings.endGroup();
2887 loadOptions();
2890 void GUI::loadOptions()
2892 _positionSource = positionSource(_options);
2893 _showPositionAction->setEnabled(_positionSource != 0);
2895 _mapView->setPalette(_options.palette);
2896 _mapView->setMapOpacity(_options.mapOpacity);
2897 _mapView->setBackgroundColor(_options.backgroundColor);
2898 _mapView->setCrosshairColor(_options.crosshairColor);
2899 _mapView->setInfoColor(_options.infoColor);
2900 _mapView->drawInfoBackground(_options.infoBackground);
2901 _mapView->setTrackWidth(_options.trackWidth);
2902 _mapView->setRouteWidth(_options.routeWidth);
2903 _mapView->setAreaWidth(_options.areaWidth);
2904 _mapView->setTrackStyle(_options.trackStyle);
2905 _mapView->setRouteStyle(_options.routeStyle);
2906 _mapView->setAreaStyle(_options.areaStyle);
2907 _mapView->setAreaOpacity(_options.areaOpacity);
2908 _mapView->setWaypointSize(_options.waypointSize);
2909 _mapView->setWaypointColor(_options.waypointColor);
2910 _mapView->setPOISize(_options.poiSize);
2911 _mapView->setPOIColor(_options.poiColor);
2912 _mapView->setRenderHint(QPainter::Antialiasing, _options.pathAntiAliasing);
2913 _mapView->setMarkerColor(_options.sliderColor);
2914 _mapView->useOpenGL(_options.useOpenGL);
2915 _mapView->setMapConfig(CRS::projection(_options.inputProjection),
2916 CRS::projection(4326, _options.outputProjection), _options.hidpiMap);
2917 _mapView->setTimeZone(_options.timeZone.zone());
2918 _mapView->setPositionSource(_positionSource);
2920 for (int i = 0; i < _tabs.count(); i++) {
2921 _tabs.at(i)->setPalette(_options.palette);
2922 _tabs.at(i)->setGraphWidth(_options.graphWidth);
2923 _tabs.at(i)->setRenderHint(QPainter::Antialiasing,
2924 _options.graphAntiAliasing);
2925 _tabs.at(i)->setSliderColor(_options.sliderColor);
2926 if (_options.useOpenGL)
2927 _tabs.at(i)->useOpenGL(true);
2930 Track::setElevationFilter(_options.elevationFilter);
2931 Track::setSpeedFilter(_options.speedFilter);
2932 Track::setHeartRateFilter(_options.heartRateFilter);
2933 Track::setCadenceFilter(_options.cadenceFilter);
2934 Track::setPowerFilter(_options.powerFilter);
2935 Track::setOutlierElimination(_options.outlierEliminate);
2936 Track::setAutomaticPause(_options.automaticPause);
2937 Track::setPauseSpeed(_options.pauseSpeed);
2938 Track::setPauseInterval(_options.pauseInterval);
2939 Track::useReportedSpeed(_options.useReportedSpeed);
2940 Track::useDEM(_options.dataUseDEM);
2941 Track::showSecondaryElevation(_options.showSecondaryElevation);
2942 Track::showSecondarySpeed(_options.showSecondarySpeed);
2943 Track::useSegments(_options.useSegments);
2944 Route::useDEM(_options.dataUseDEM);
2945 Route::showSecondaryElevation(_options.showSecondaryElevation);
2946 Waypoint::useDEM(_options.dataUseDEM);
2947 Waypoint::showSecondaryElevation(_options.showSecondaryElevation);
2949 Downloader::enableHTTP2(_options.enableHTTP2);
2950 Downloader::setTimeout(_options.connectionTimeout);
2952 QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
2953 DEM::setCacheSize(_options.demCache * 1024);
2955 _poi->setRadius(_options.poiRadius);
2957 _dem->setUrl(_options.demURL);
2958 if (_options.demAuthorization)
2959 _dem->setAuthorization(Authorization(_options.demUsername,
2960 _options.demPassword));
2962 _dataDir = _options.dataPath;
2963 _mapDir = _options.mapsPath;
2964 _poiDir = _options.poiPath;
2967 void GUI::updateOptions(const Options &options)
2969 #define SET_VIEW_OPTION(option, action) \
2970 if (options.option != _options.option) \
2971 _mapView->action(options.option)
2972 #define SET_TAB_OPTION(option, action) \
2973 if (options.option != _options.option) \
2974 for (int i = 0; i < _tabs.count(); i++) \
2975 _tabs.at(i)->action(options.option)
2976 #define SET_TRACK_OPTION(option, action) \
2977 if (options.option != _options.option) { \
2978 Track::action(options.option); \
2979 reload = true; \
2981 #define SET_ROUTE_OPTION(option, action) \
2982 if (options.option != _options.option) { \
2983 Route::action(options.option); \
2984 reload = true; \
2986 #define SET_WAYPOINT_OPTION(option, action) \
2987 if (options.option != _options.option) { \
2988 Waypoint::action(options.option); \
2989 reload = true; \
2992 bool reload = false;
2994 SET_VIEW_OPTION(palette, setPalette);
2995 SET_VIEW_OPTION(mapOpacity, setMapOpacity);
2996 SET_VIEW_OPTION(backgroundColor, setBackgroundColor);
2997 SET_VIEW_OPTION(trackWidth, setTrackWidth);
2998 SET_VIEW_OPTION(routeWidth, setRouteWidth);
2999 SET_VIEW_OPTION(areaWidth, setAreaWidth);
3000 SET_VIEW_OPTION(trackStyle, setTrackStyle);
3001 SET_VIEW_OPTION(routeStyle, setRouteStyle);
3002 SET_VIEW_OPTION(areaStyle, setAreaStyle);
3003 SET_VIEW_OPTION(areaOpacity, setAreaOpacity);
3004 SET_VIEW_OPTION(waypointSize, setWaypointSize);
3005 SET_VIEW_OPTION(waypointColor, setWaypointColor);
3006 SET_VIEW_OPTION(poiSize, setPOISize);
3007 SET_VIEW_OPTION(poiColor, setPOIColor);
3008 SET_VIEW_OPTION(pathAntiAliasing, useAntiAliasing);
3009 SET_VIEW_OPTION(useOpenGL, useOpenGL);
3010 SET_VIEW_OPTION(sliderColor, setMarkerColor);
3011 SET_VIEW_OPTION(crosshairColor, setCrosshairColor);
3012 SET_VIEW_OPTION(infoColor, setInfoColor);
3013 SET_VIEW_OPTION(infoBackground, drawInfoBackground);
3015 if (options.plugin != _options.plugin
3016 || options.pluginParams.value(options.plugin)
3017 != _options.pluginParams.value(_options.plugin)) {
3018 QGeoPositionInfoSource *source = positionSource(options);
3019 _showPositionAction->setEnabled(source != 0);
3020 _mapView->setPositionSource(source);
3021 delete _positionSource;
3022 _positionSource = source;
3025 if (options.hidpiMap != _options.hidpiMap
3026 || options.outputProjection != _options.outputProjection
3027 || options.inputProjection != _options.inputProjection)
3028 _mapView->setMapConfig(CRS::projection(options.inputProjection),
3029 CRS::projection(4326, options.outputProjection), options.hidpiMap);
3031 if (options.timeZone != _options.timeZone) {
3032 _mapView->setTimeZone(options.timeZone.zone());
3033 _dateRange.first = _dateRange.first.toTimeZone(options.timeZone.zone());
3034 _dateRange.second = _dateRange.second.toTimeZone(options.timeZone.zone());
3037 SET_TAB_OPTION(palette, setPalette);
3038 SET_TAB_OPTION(graphWidth, setGraphWidth);
3039 SET_TAB_OPTION(graphAntiAliasing, useAntiAliasing);
3040 SET_TAB_OPTION(useOpenGL, useOpenGL);
3041 SET_TAB_OPTION(sliderColor, setSliderColor);
3043 SET_TRACK_OPTION(elevationFilter, setElevationFilter);
3044 SET_TRACK_OPTION(speedFilter, setSpeedFilter);
3045 SET_TRACK_OPTION(heartRateFilter, setHeartRateFilter);
3046 SET_TRACK_OPTION(cadenceFilter, setCadenceFilter);
3047 SET_TRACK_OPTION(powerFilter, setPowerFilter);
3048 SET_TRACK_OPTION(outlierEliminate, setOutlierElimination);
3049 SET_TRACK_OPTION(automaticPause, setAutomaticPause);
3050 SET_TRACK_OPTION(pauseSpeed, setPauseSpeed);
3051 SET_TRACK_OPTION(pauseInterval, setPauseInterval);
3052 SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed);
3053 SET_TRACK_OPTION(dataUseDEM, useDEM);
3054 SET_TRACK_OPTION(showSecondaryElevation, showSecondaryElevation);
3055 SET_TRACK_OPTION(showSecondarySpeed, showSecondarySpeed);
3056 SET_TRACK_OPTION(useSegments, useSegments);
3058 SET_ROUTE_OPTION(dataUseDEM, useDEM);
3059 SET_ROUTE_OPTION(showSecondaryElevation, showSecondaryElevation);
3061 SET_WAYPOINT_OPTION(dataUseDEM, useDEM);
3062 SET_WAYPOINT_OPTION(showSecondaryElevation, showSecondaryElevation);
3064 if (options.poiRadius != _options.poiRadius)
3065 _poi->setRadius(options.poiRadius);
3067 if (options.demURL != _options.demURL)
3068 _dem->setUrl(options.demURL);
3069 if (options.demAuthorization != _options.demAuthorization
3070 || options.demUsername != _options.demUsername
3071 || options.demPassword != _options.demPassword)
3072 _dem->setAuthorization(options.demAuthorization
3073 ? Authorization(options.demUsername, options.demPassword)
3074 : Authorization());
3076 if (options.pixmapCache != _options.pixmapCache)
3077 QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
3078 if (options.demCache != _options.demCache)
3079 DEM::setCacheSize(options.demCache * 1024);
3081 if (options.connectionTimeout != _options.connectionTimeout)
3082 Downloader::setTimeout(options.connectionTimeout);
3083 if (options.enableHTTP2 != _options.enableHTTP2)
3084 Downloader::enableHTTP2(options.enableHTTP2);
3086 if (options.dataPath != _options.dataPath)
3087 _dataDir = options.dataPath;
3088 if (options.mapsPath != _options.mapsPath)
3089 _mapDir = options.mapsPath;
3090 if (options.poiPath != _options.poiPath)
3091 _poiDir = options.poiPath;
3093 if (reload)
3094 reloadFiles();
3096 _options = options;
3098 updateDEMDownloadAction();
3101 void GUI::loadInitialMaps(const QString &selected)
3103 // Load the maps
3104 QString mapDir(ProgramPaths::mapDir());
3105 if (mapDir.isNull())
3106 return;
3108 TreeNode<Map*> maps(MapList::loadMaps(mapDir, _mapView->inputProjection()));
3109 createMapNodeMenu(createMapActionsNode(maps), _mapMenu, _mapsEnd);
3111 // Select the active map according to the user settings
3112 QAction *ma = mapAction(selected);
3113 if (ma) {
3114 ma->trigger();
3115 _showMapAction->setEnabled(true);
3116 _clearMapCacheAction->setEnabled(true);
3120 void GUI::loadInitialPOIs(const QStringList &disabled)
3122 // Load the POI files
3123 QString poiDir(ProgramPaths::poiDir());
3124 if (poiDir.isNull())
3125 return;
3127 TreeNode<QString> poiFiles(_poi->loadDir(poiDir));
3128 createPOINodeMenu(createPOIActionsNode(poiFiles), _poiMenu, _poisEnd);
3130 // Enable/disable the files according to the user settings
3131 QList<QAction*> poiActions(_poisActionGroup->actions());
3132 for (int i = 0; i < poiActions.count(); i++)
3133 poiActions.at(i)->setChecked(true);
3134 for (int i = 0; i < disabled.size(); i++) {
3135 const QString &file = disabled.at(i);
3136 if (_poi->enableFile(file, false)) {
3137 for (int j = 0; j < poiActions.size(); j++)
3138 if (poiActions.at(j)->data().toString() == file)
3139 poiActions.at(j)->setChecked(false);
3143 _selectAllPOIAction->setEnabled(!poiActions.isEmpty());
3144 _unselectAllPOIAction->setEnabled(!poiActions.isEmpty());
3147 #ifndef Q_OS_ANDROID
3148 void GUI::loadRecentFiles(const QStringList &files)
3150 QAction *before = _recentFilesEnd;
3152 for (int i = 0; i < files.size(); i++) {
3153 QAction *a = new QAction(files.at(i), _recentFilesActionGroup);
3154 _recentFilesMenu->insertAction(before, a);
3155 before = a;
3158 if (!files.isEmpty())
3159 _recentFilesMenu->setEnabled(true);
3161 #endif // Q_OS_ANDROID
3163 QAction *GUI::mapAction(const QString &name)
3165 QList<QAction *> maps(_mapsActionGroup->actions());
3167 // Last map
3168 for (int i = 0; i < maps.count(); i++) {
3169 Map *map = maps.at(i)->data().value<Map*>();
3170 if (map->name() == name && map->isReady())
3171 return maps.at(i);
3174 // Any usable map
3175 for (int i = 0; i < maps.count(); i++) {
3176 Map *map = maps.at(i)->data().value<Map*>();
3177 if (map->isReady())
3178 return maps.at(i);
3181 return 0;
3184 Units GUI::units() const
3186 return _imperialUnitsAction->isChecked() ? Imperial
3187 : _nauticalUnitsAction->isChecked() ? Nautical : Metric;
3190 qreal GUI::distance() const
3192 qreal dist = 0;
3194 if (_showTracksAction->isChecked())
3195 dist += _trackDistance;
3196 if (_showRoutesAction->isChecked())
3197 dist += _routeDistance;
3199 return dist;
3202 qreal GUI::time() const
3204 return (_showTracksAction->isChecked()) ? _time : 0;
3207 qreal GUI::movingTime() const
3209 return (_showTracksAction->isChecked()) ? _movingTime : 0;
3212 void GUI::show()
3214 QMainWindow::show();
3216 QWindow *w = windowHandle();
3217 connect(w->screen(), &QScreen::logicalDotsPerInchChanged, this,
3218 &GUI::logicalDotsPerInchChanged);
3219 connect(w, &QWindow::screenChanged, this, &GUI::screenChanged);
3221 _mapView->fitContentToSize();
3224 void GUI::screenChanged(QScreen *screen)
3226 _mapView->setDevicePixelRatio(devicePixelRatioF());
3228 disconnect(SIGNAL(logicalDotsPerInchChanged(qreal)), this,
3229 SLOT(logicalDotsPerInchChanged(qreal)));
3230 connect(screen, &QScreen::logicalDotsPerInchChanged, this,
3231 &GUI::logicalDotsPerInchChanged);
3234 void GUI::logicalDotsPerInchChanged(qreal dpi)
3236 Q_UNUSED(dpi)
3238 _mapView->setDevicePixelRatio(devicePixelRatioF());