1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-17 20:34:23 +02:00

Compare commits

...

14 Commits

Author SHA1 Message Date
306116dbde Fixed possible crash on map unload 2022-06-02 23:22:34 +02:00
ca1c576562 Fixed Android build 2022-06-02 22:08:40 +02:00
feb1650ed0 Allow tile cache sizes up to 2GB 2022-06-02 19:14:34 +02:00
e6e41c846c Increase the default tile image cache size
On big IMG maps that are "digitaly" zoomed out, the tile memory may not be
sufficient for the whole display area which leads to "cache ping-pong", image
artefacts and 100% CPU usage. 512MB RAM should be available for everyone on
the desktop these days...
2022-06-02 19:03:12 +02:00
20adecec89 Code cleanup 2022-06-02 19:02:46 +02:00
8cb08d05ca Version++ 2022-06-02 18:35:44 +02:00
7bd03494db Code cleanup 2022-06-02 18:35:27 +02:00
7ba19b8c0a Use asynchronous tiles rendering in IMG maps
On Android devices, the rendering is very slow so use the same approach as
already used in Mapsforge maps.
2022-06-02 18:31:40 +02:00
25869200db Fixed broken map zooming using the mouse wheel
(introduced in 11.0)
2022-06-02 18:30:19 +02:00
af0bb2e268 Updated features, build and download info 2022-05-30 19:13:12 +02:00
844607f6ce Added missing QT SVG devel package dependency 2022-05-28 14:14:59 +02:00
ba0b7c62a4 Android port 2022-05-28 14:05:14 +02:00
e701ad0ed0 Fixed broken PDF/PNG export of MApsforge maps 2022-05-25 23:04:20 +02:00
c3ba8f1f0d Fixed broken map plotting on systems with HiDpi displays 2022-05-25 23:03:30 +02:00
49 changed files with 1088 additions and 148 deletions

View File

@ -1,4 +1,4 @@
version: 10.8.{build}
version: 11.1.{build}
configuration:
- Release

View File

@ -15,7 +15,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install qtbase5-dev qtbase5-private-dev qtbase5-dev-tools qt5-qmake qttools5-dev-tools libqt5opengl5-dev qtpositioning5-dev
sudo apt-get install qtbase5-dev qtbase5-private-dev qtbase5-dev-tools qt5-qmake qttools5-dev-tools libqt5opengl5-dev qtpositioning5-dev libqt5svg5-dev
- name: Create localization
run: lrelease gpxsee.pro
- name: Configure build

View File

@ -21,13 +21,13 @@ GPS log file formats.
* Full-screen mode.
* HiDPI/Retina displays & maps support.
* Real-time GPS position.
* Native GUI for Windows, Mac OS X and Linux.
* Windows, macOS, Linux and Android builds.
![GPXSee - Linux](https://a.fsdn.com/con/app/proj/gpxsee/screenshots/linux2.png)
## Build
Build requirements:
* Qt5 >= 5.11 or Qt 6.x*
* Qt5 >= 5.11 or Qt6* >= 6.2
* C++11 or newer compiler (tested: msvc2017, gcc 7.5.0, clang/Apple LLVM version
10.0.0)
@ -42,8 +42,9 @@ _* Qt6 builds do not support other encodings than UTF-X and iso8859-1 in XML
files as support for such files was dropped in Qt6._
## Download
* [Windows & OS X builds](http://sourceforge.net/projects/gpxsee)
* [Linux packages](http://software.opensuse.org/download.html?project=home%3Atumic%3AGPXSee&package=gpxsee)
* [Windows & OS X builds](https://sourceforge.net/projects/gpxsee)
* [Linux packages](https://software.opensuse.org/download.html?project=home%3Atumic%3AGPXSee&package=gpxsee)
* [Android APKs](https://play.google.com/store/apps/details?id=org.gpxsee.gpxsee)
## Changelog
[Changelog](https://build.opensuse.org/package/view_file/home:tumic:GPXSee/gpxsee/gpxsee.changes)

View File

@ -1,9 +1,9 @@
unix:!macx {
unix:!macx:!android {
TARGET = gpxsee
} else {
TARGET = GPXSee
}
VERSION = 10.8
VERSION = 11.1
QT += core \
gui \
@ -13,7 +13,8 @@ QT += core \
concurrent \
widgets \
printsupport \
positioning
positioning \
svg
greaterThan(QT_MAJOR_VERSION, 5) {
QT += openglwidgets \
core5compat
@ -24,6 +25,7 @@ INCLUDEPATH += ./src
HEADERS += src/common/config.h \
src/GUI/crosshairitem.h \
src/GUI/motioninfoitem.h \
src/GUI/navigationwidget.h \
src/GUI/pluginparameters.h \
src/common/garmin.h \
src/common/coordinates.h \
@ -246,6 +248,7 @@ HEADERS += src/common/config.h \
SOURCES += src/main.cpp \
src/GUI/crosshairitem.cpp \
src/GUI/motioninfoitem.cpp \
src/GUI/navigationwidget.cpp \
src/GUI/pluginparameters.cpp \
src/common/coordinates.cpp \
src/common/rectc.cpp \
@ -504,7 +507,7 @@ win32 {
NOGDI
}
unix:!macx {
unix:!macx:!android {
isEmpty(PREFIX):PREFIX = /usr/local
maps.files = $$files(pkg/maps/*)
@ -524,3 +527,39 @@ unix:!macx {
target.path = $$PREFIX/bin
INSTALLS += target maps csv symbols locale icon desktop mime
}
android {
defineReplace(versionCode) {
segments = $$split(1, ".")
for (segment, segments): \
vCode = "$$first(vCode)$$format_number($$segment, width=3 zeropad)"
contains(ANDROID_TARGET_ARCH, armeabi-v7a): \
suffix = 0
contains(ANDROID_TARGET_ARCH, arm64-v8a): \
suffix = 1
contains(ANDROID_TARGET_ARCH, x86): \
suffix = 2
contains(ANDROID_TARGET_ARCH, x86_64): \
suffix = 3
return($$first(vCode)$$first(suffix))
}
include($$OPENSSL_PATH/openssl.pri)
ANDROID_VERSION_NAME = $$VERSION
ANDROID_VERSION_CODE = $$versionCode($$ANDROID_VERSION_NAME)
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/pkg/android
DISTFILES += \
pkg/android/AndroidManifest.xml \
pkg/android/build.gradle \
pkg/android/res/values/libs.xml
maps.files = $$files(pkg/maps/*)
maps.path = /assets/maps
csv.files = $$files(pkg/csv/*)
csv.path = /assets/csv
symbols.files = $$files(icons/symbols/*.png)
symbols.path = /assets/symbols
INSTALLS += maps csv symbols
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.gpxsee.gpxsee" android:installLocation="auto" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:versionName="-- %%INSERT_VERSION_NAME%% --">
<!-- %%INSERT_PERMISSIONS -->
<!-- %%INSERT_FEATURES -->
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/>
<application android:name="org.qtproject.qt.android.bindings.QtApplication" android:extractNativeLibs="true" android:hardwareAccelerated="true" android:label="-- %%INSERT_APP_NAME%% --" android:requestLegacyExternalStorage="true" android:allowNativeHeapPointerTagging="false" android:icon="@drawable/icon">
<activity android:name="org.qtproject.qt.android.bindings.QtActivity" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:label="-- %%INSERT_APP_NAME%% --" android:launchMode="singleTop" android:screenOrientation="unspecified">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.arguments" android:value="-- %%INSERT_APP_ARGUMENTS%% --"/>
<meta-data android:name="android.app.extract_android_style" android:value="minimal"/>
</activity>
</application>
</manifest>

78
pkg/android/build.gradle Normal file
View File

@ -0,0 +1,78 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.2'
}
}
repositories {
google()
mavenCentral()
}
apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
tasks.withType(JavaCompile) {
options.incremental = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
defaultConfig {
resConfig "en"
minSdkVersion qtMinSdkVersion
targetSdkVersion qtTargetSdkVersion
ndk.abiFilters = qtTargetAbiList.split(",")
}
}

View File

@ -0,0 +1,14 @@
# Project-wide Gradle settings.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# Enable building projects in parallel
org.gradle.parallel=true
# Gradle caching allows reusing the build artifacts from a previous
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
<array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
</array>
<array name="qt_libs">
<!-- %%INSERT_QT_LIBS%% -->
</array>
<array name="load_local_libs">
<!-- %%INSERT_LOCAL_LIBS%% -->
</array>
<string name="static_init_classes"><!-- %%INSERT_INIT_CLASSES%% --></string>
<string name="use_local_qt_libs"><!-- %%USE_LOCAL_QT_LIBS%% --></string>
<string name="bundle_local_qt_libs"><!-- %%BUNDLE_LOCAL_QT_LIBS%% --></string>
</resources>

View File

@ -37,7 +37,7 @@ Unicode true
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "10.8"
!define VERSION "11.1"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"

View File

@ -61,6 +61,10 @@ App::App(int &argc, char **argv) : QApplication(argc, argv)
Waypoint::loadSymbolIcons(ProgramPaths::symbolsDir());
_gui = new GUI();
#ifdef Q_OS_ANDROID
connect(this, &App::applicationStateChanged, this, &App::appStateChanged);
#endif // Q_OS_ANDROID
}
App::~App()
@ -93,6 +97,14 @@ int App::run()
return exec();
}
#ifdef Q_OS_ANDROID
void App::appStateChanged(Qt::ApplicationState state)
{
if (state == Qt::ApplicationSuspended)
_gui->writeSettings();
}
#endif // Q_OS_ANDROID
bool App::event(QEvent *event)
{
if (event->type() == QEvent::FileOpen) {

View File

@ -5,7 +5,7 @@
class GUI;
class App : QApplication
class App : public QApplication
{
Q_OBJECT
@ -17,6 +17,11 @@ public:
protected:
bool event(QEvent *event);
#ifdef Q_OS_ANDROID
private slots:
void appStateChanged(Qt::ApplicationState state);
#endif // Q_OS_ANDROID
private:
void loadDatums();
void loadPCSs();

View File

@ -24,10 +24,17 @@ void CadenceGraph::setInfo()
if (_showTracks) {
QLocale l(QLocale::system());
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Average"), l.toString(avg() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
} else
clearInfo();
}

View File

@ -52,17 +52,19 @@ void ColorBox::paintEvent(QPaintEvent *event)
void ColorBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() != Qt::LeftButton)
return;
QColorDialog::ColorDialogOptions options = _alpha
? QColorDialog::ColorDialogOptions(QColorDialog::ShowAlphaChannel)
: QColorDialog::ColorDialogOptions();
QColor color = QColorDialog::getColor(_color, this, QString(), options);
if (color.isValid()) {
_color = color;
update();
emit colorChanged(_color);
if (event->button() == Qt::LeftButton) {
QColorDialog::ColorDialogOptions options = _alpha
? QColorDialog::ColorDialogOptions(QColorDialog::ShowAlphaChannel)
: QColorDialog::ColorDialogOptions();
QColor color = QColorDialog::getColor(_color, this, QString(), options);
if (color.isValid()) {
_color = color;
update();
emit colorChanged(_color);
}
}
QWidget::mousePressEvent(event);
}
void ColorBox::setColor(const QColor &color)

View File

@ -9,10 +9,12 @@
DirSelectWidget::DirSelectWidget(QWidget *parent) : QWidget(parent)
{
QFontMetrics fm(QApplication::font());
_edit = new QLineEdit();
#ifndef Q_OS_ANDROID
QFontMetrics fm(QApplication::font());
_edit->setMinimumWidth(fm.averageCharWidth() * (QDir::homePath().length()
+ 12));
#endif // Q_OS_ANDROID
_edit->setPlaceholderText(tr("System default"));
#ifdef Q_OS_WIN32
_button = new QPushButton("...");

View File

@ -54,6 +54,16 @@ void ElevationGraph::setInfo()
else {
QLocale l(QLocale::system());
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Up"), l.toString(ascent() * yScale(), 'f', 0)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Down"), l.toString(descent() * yScale(), 'f', 0)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 0)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Min"), l.toString(min() * yScale(), 'f', 0)
+ UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Ascent"), l.toString(ascent() * yScale(),
'f', 0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Descent"), l.toString(descent() * yScale(),
@ -62,6 +72,7 @@ void ElevationGraph::setInfo()
0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Minimum"), l.toString(min() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
}
}

View File

@ -5,14 +5,25 @@
FileBrowser::FileBrowser(QObject *parent) : QObject(parent)
{
#ifndef Q_OS_ANDROID
_watcher = new QFileSystemWatcher(this);
connect(_watcher, &QFileSystemWatcher::directoryChanged, this,
&FileBrowser::reloadDirectory);
#endif // Q_OS_ANDROID
_index = -1;
}
#ifdef Q_OS_ANDROID
void FileBrowser::setCurrentDir(const QString &path)
{
QDir dir(path);
_files = dir.entryInfoList(_filter, QDir::Files);
_index = _files.empty() ? -1 : 0;
emit listChanged();
}
#else // Q_OS_ANDROID
void FileBrowser::setCurrent(const QString &path)
{
QFileInfo file(path);
@ -28,6 +39,7 @@ void FileBrowser::setCurrent(const QString &path)
_index = _files.empty() ? -1 : _files.indexOf(file);
}
#endif // Q_OS_ANDROID
void FileBrowser::setFilter(const QStringList &filter)
{
@ -46,6 +58,11 @@ bool FileBrowser::isFirst() const
return (_files.size() > 0 && _index == 0);
}
QString FileBrowser::current()
{
return (_index >= 0) ? _files.at(_index).absoluteFilePath() : QString();
}
QString FileBrowser::next()
{
if (_index < 0 || _index == _files.size() - 1)

View File

@ -14,9 +14,14 @@ class FileBrowser : public QObject
public:
FileBrowser(QObject *parent = 0);
void setFilter(const QStringList &filter);
#ifdef Q_OS_ANDROID
void setCurrentDir(const QString &path);
#else // Q_OS_ANDROID
void setCurrent(const QString &path);
#endif // Q_OS_ANDROID
void setFilter(const QStringList &filter);
QString current();
QString next();
QString prev();
QString last();
@ -32,7 +37,9 @@ private slots:
void reloadDirectory(const QString &path);
private:
#ifndef Q_OS_ANDROID
QFileSystemWatcher *_watcher;
#endif // Q_OS_ANDROID
QStringList _filter;
QFileInfoList _files;
int _index;

View File

@ -10,10 +10,12 @@
FileSelectWidget::FileSelectWidget(QWidget *parent) : QWidget(parent)
{
QFontMetrics fm(QApplication::font());
_edit = new QLineEdit();
#ifndef Q_OS_ANDROID
QFontMetrics fm(QApplication::font());
_edit->setMinimumWidth(fm.averageCharWidth() * (QDir::homePath().length()
+ 12));
#endif // Q_OS_ANDROID
#ifdef Q_OS_WIN32
_button = new QPushButton("...");
_button->setMaximumWidth(_button->sizeHint().width() / 2);

View File

@ -24,12 +24,21 @@ void GearRatioGraph::setInfo()
if (_showTracks) {
QLocale l(QLocale::system());
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Top"), l.toString(top() * yScale(), 'f', 2)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Min"), l.toString(min() * yScale(), 'f', 2)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 2)
+ UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Most used"), l.toString(top() * yScale(),
'f', 2) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Minimum"), l.toString(min() * yScale(), 'f',
2) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale(), 'f',
2) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
} else
clearInfo();
}

View File

@ -53,6 +53,7 @@
#include "mapitem.h"
#include "mapaction.h"
#include "poiaction.h"
#include "navigationwidget.h"
#include "gui.h"
@ -72,7 +73,11 @@ GUI::GUI()
createStatusBar();
createActions();
createMenus();
#ifdef Q_OS_ANDROID
createNavigation();
#else // Q_OS_ANDROID
createToolBars();
#endif // Q_OS_ANDROID
createBrowser();
_splitter = new QSplitter();
@ -83,6 +88,7 @@ GUI::GUI()
_splitter->setContentsMargins(0, 0, 0, 0);
_splitter->setStretchFactor(0, 255);
_splitter->setStretchFactor(1, 1);
setCentralWidget(_splitter);
setWindowIcon(QIcon(APP_ICON));
@ -181,19 +187,23 @@ void GUI::createActions()
_navigationActionGroup->setEnabled(false);
// General actions
#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
_exitAction = new QAction(QIcon(QUIT_ICON), tr("Quit"), this);
_exitAction->setShortcut(QUIT_SHORTCUT);
_exitAction->setMenuRole(QAction::QuitRole);
connect(_exitAction, &QAction::triggered, this, &GUI::close);
addAction(_exitAction);
#endif // Q_OS_MAC + Q_OS_ANDROID
// Help & About
_pathsAction = new QAction(tr("Paths"), this);
_pathsAction->setMenuRole(QAction::NoRole);
connect(_pathsAction, &QAction::triggered, this, &GUI::paths);
#ifndef Q_OS_ANDROID
_keysAction = new QAction(tr("Keyboard controls"), this);
_keysAction->setMenuRole(QAction::NoRole);
connect(_keysAction, &QAction::triggered, this, &GUI::keys);
#endif // Q_OS_ANDROID
_aboutAction = new QAction(QIcon(APP_ICON), tr("About GPXSee"), this);
_aboutAction->setMenuRole(QAction::AboutRole);
connect(_aboutAction, &QAction::triggered, this, &GUI::about);
@ -205,6 +215,12 @@ void GUI::createActions()
connect(_openFileAction, &QAction::triggered, this,
QOverload<>::of(&GUI::openFile));
addAction(_openFileAction);
#ifdef Q_OS_ANDROID
_openDirAction = new QAction(QIcon(OPEN_FILE_ICON), tr("Open directory..."),
this);
_openDirAction->setMenuRole(QAction::NoRole);
connect(_openDirAction, &QAction::triggered, this, &GUI::openDir);
#endif // Q_OS_ANDROID
_printFileAction = new QAction(QIcon(PRINT_FILE_ICON), tr("Print..."),
this);
_printFileAction->setMenuRole(QAction::NoRole);
@ -459,10 +475,12 @@ void GUI::createActions()
&GUI::showGraphSliderInfo);
// Settings actions
#ifndef Q_OS_ANDROID
_showToolbarsAction = new QAction(tr("Show toolbars"), this);
_showToolbarsAction->setMenuRole(QAction::NoRole);
_showToolbarsAction->setCheckable(true);
connect(_showToolbarsAction, &QAction::triggered, this, &GUI::showToolbars);
#endif // Q_OS_ANDROID
ag = new QActionGroup(this);
ag->setExclusive(true);
_totalTimeAction = new QAction(tr("Total time"), this);
@ -514,6 +532,7 @@ void GUI::createActions()
_dmsAction->setCheckable(true);
_dmsAction->setActionGroup(ag);
connect(_dmsAction, &QAction::triggered, this, &GUI::setDMS);
#ifndef Q_OS_ANDROID
_fullscreenAction = new QAction(QIcon(FULLSCREEN_ICON),
tr("Fullscreen mode"), this);
_fullscreenAction->setMenuRole(QAction::NoRole);
@ -521,11 +540,13 @@ void GUI::createActions()
_fullscreenAction->setShortcut(FULLSCREEN_SHORTCUT);
connect(_fullscreenAction, &QAction::triggered, this, &GUI::showFullscreen);
addAction(_fullscreenAction);
#endif // Q_OS_ANDROID
_openOptionsAction = new QAction(tr("Options..."), this);
_openOptionsAction->setMenuRole(QAction::PreferencesRole);
connect(_openOptionsAction, &QAction::triggered, this, &GUI::openOptions);
// Navigation actions
#ifndef Q_OS_ANDROID
_nextAction = new QAction(QIcon(NEXT_FILE_ICON), tr("Next"), this);
_nextAction->setActionGroup(_navigationActionGroup);
_nextAction->setMenuRole(QAction::NoRole);
@ -542,6 +563,7 @@ void GUI::createActions()
_firstAction->setMenuRole(QAction::NoRole);
_firstAction->setActionGroup(_navigationActionGroup);
connect(_firstAction, &QAction::triggered, this, &GUI::first);
#endif // Q_OS_ANDROID
}
void GUI::createMapNodeMenu(const TreeNode<MapAction*> &node, QMenu *menu,
@ -574,8 +596,13 @@ void GUI::createMenus()
{
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(_openFileAction);
#ifdef Q_OS_ANDROID
fileMenu->addAction(_openDirAction);
#endif // Q_OS_ANDROID
fileMenu->addSeparator();
#ifndef Q_OS_ANDROID
fileMenu->addAction(_printFileAction);
#endif // Q_OS_ANDROID
fileMenu->addAction(_exportPDFFileAction);
fileMenu->addAction(_exportPNGFileAction);
fileMenu->addSeparator();
@ -583,10 +610,10 @@ void GUI::createMenus()
fileMenu->addSeparator();
fileMenu->addAction(_reloadFileAction);
fileMenu->addAction(_closeFileAction);
#ifndef Q_OS_MAC
#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
fileMenu->addSeparator();
fileMenu->addAction(_exitAction);
#endif // Q_OS_MAC
#endif // Q_OS_MAC + Q_OS_ANDROID
_mapMenu = menuBar()->addMenu(tr("&Map"));
_mapsEnd = _mapMenu->addSeparator();
@ -659,18 +686,31 @@ void GUI::createMenus()
coordinatesMenu->addAction(_degreesMinutesAction);
coordinatesMenu->addAction(_dmsAction);
settingsMenu->addSeparator();
#ifndef Q_OS_ANDROID
settingsMenu->addAction(_showToolbarsAction);
settingsMenu->addAction(_fullscreenAction);
settingsMenu->addSeparator();
#endif // Q_OS_ANDROID
settingsMenu->addAction(_openOptionsAction);
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(_pathsAction);
#ifndef Q_OS_ANDROID
helpMenu->addAction(_keysAction);
#endif // Q_OS_ANDROID
helpMenu->addSeparator();
helpMenu->addAction(_aboutAction);
}
#ifdef Q_OS_ANDROID
void GUI::createNavigation()
{
_navigation = new NavigationWidget(_mapView);
connect(_navigation, &NavigationWidget::next, this, &GUI::next);
connect(_navigation, &NavigationWidget::prev, this, &GUI::prev);
}
#else // Q_OS_ANDROID
void GUI::createToolBars()
{
int is = style()->pixelMetric(QStyle::PM_ToolBarIconSize);
@ -706,6 +746,7 @@ void GUI::createToolBars()
_navigationToolBar->addAction(_nextAction);
_navigationToolBar->addAction(_lastAction);
}
#endif // Q_OS_ANDROID
void GUI::createMapView()
{
@ -713,7 +754,11 @@ void GUI::createMapView()
_mapView = new MapView(_map, _poi, 0, this);
_mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Expanding));
#ifdef Q_OS_ANDROID
_mapView->setMinimumHeight(100);
#else // Q_OS_ANDROID
_mapView->setMinimumHeight(200);
#endif // Q_OS_ANDROID
#ifdef Q_OS_WIN32
_mapView->setFrameShape(QFrame::NoFrame);
#endif // Q_OS_WIN32
@ -765,7 +810,16 @@ void GUI::about()
QUrl homepage(APP_HOMEPAGE);
msgBox.setWindowTitle(tr("About GPXSee"));
msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p><p>" + tr("Version %1")
#ifdef Q_OS_ANDROID
msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p>" + tr("Version %1")
.arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture()
+ ", Qt " + QT_VERSION_STR + ")") + "</p><p>"
+ tr("GPXSee is distributed under the terms of the GNU General Public "
"License version 3. For more info about GPXSee visit the project "
"homepage at %1.").arg("<a href=\"" + homepage.toString() + "\">"
+ homepage.toString(QUrl::RemoveScheme).mid(2) + "</a>") + "</p>");
#else // Q_OS_ANDROID
msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p>" + tr("Version %1")
.arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture()
+ ", Qt " + QT_VERSION_STR + ")") + "</p>");
msgBox.setInformativeText("<table width=\"300\"><tr><td>"
@ -778,10 +832,12 @@ void GUI::about()
QIcon icon = msgBox.windowIcon();
QSize size = icon.actualSize(QSize(64, 64));
msgBox.setIconPixmap(icon.pixmap(size));
#endif // Q_OS_ANDROID
msgBox.exec();
}
#ifndef Q_OS_ANDROID
void GUI::keys()
{
QMessageBox msgBox(this);
@ -820,12 +876,30 @@ void GUI::keys()
msgBox.exec();
}
#endif // Q_OS_ANDROID
void GUI::paths()
{
QMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Paths"));
#ifdef Q_OS_ANDROID
msgBox.setText(
+ "<small><b>" + tr("Map directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::mapDir(true)) + "<br><br><b>"
+ tr("POI directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::poiDir(true)) + "<br><br><b>"
+ tr("GCS/PCS directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::csvDir(true)) + "<br><br><b>"
+ tr("DEM directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::demDir(true)) + "<br><br><b>"
+ tr("Styles directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::styleDir(true)) + "<br><br><b>"
+ tr("Symbols directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::symbolsDir(true)) + "<br><br><b>"
+ tr("Tile cache directory:") + "</b><br>"
+ QDir::cleanPath(ProgramPaths::tilesDir()) + "</small>");
#else // Q_OS_ANDROID
msgBox.setText("<h3>" + tr("Paths") + "</h3>");
msgBox.setInformativeText(
"<style>td {white-space: pre; padding-right: 1em;}</style><table><tr><td>"
@ -844,14 +918,20 @@ void GUI::paths()
+ tr("Tile cache directory:") + "</td><td><code>"
+ QDir::cleanPath(ProgramPaths::tilesDir()) + "</code></td></tr></table>"
);
#endif // Q_OS_ANDROID
msgBox.exec();
}
void GUI::openFile()
{
#ifdef Q_OS_ANDROID
QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"),
_dataDir));
#else // Q_OS_ANDROID
QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"),
_dataDir, Data::formats()));
#endif // Q_OS_ANDROID
for (int i = 0; i < files.size(); i++)
openFile(files.at(i));
@ -859,6 +939,19 @@ void GUI::openFile()
_dataDir = QFileInfo(files.last()).path();
}
#ifdef Q_OS_ANDROID
void GUI::openDir()
{
QString dir(QFileDialog::getExistingDirectory(this, tr("Open directory"),
_dataDir));
if (!dir.isEmpty()) {
_browser->setCurrentDir(dir);
openFile(_browser->current());
}
}
#endif // Q_OS_ANDROID
bool GUI::openFile(const QString &fileName, bool silent)
{
if (_files.contains(fileName))
@ -868,7 +961,9 @@ bool GUI::openFile(const QString &fileName, bool silent)
return false;
_files.append(fileName);
#ifndef Q_OS_ANDROID
_browser->setCurrent(fileName);
#endif // Q_OS_ANDROID
_fileActionGroup->setEnabled(true);
// Explicitly enable the reload action as it may be disabled by loadMapDir()
_reloadFileAction->setEnabled(true);
@ -898,7 +993,7 @@ bool GUI::loadFile(const QString &fileName, bool silent)
_fileActionGroup->setEnabled(false);
QString error = tr("Error loading data file:") + "\n\n"
+ fileName + "\n\n" + data.errorString();
+ Util::displayName(fileName) + "\n\n" + data.errorString();
if (data.errorLine())
error.append("\n" + tr("Line: %1").arg(data.errorLine()));
QMessageBox::critical(this, APP_NAME, error);
@ -967,8 +1062,13 @@ void GUI::loadData(const Data &data)
void GUI::openPOIFile()
{
#ifdef Q_OS_ANDROID
QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"),
_poiDir));
#else // Q_OS_ANDROID
QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"),
_poiDir, Data::formats()));
#endif // Q_OS_ANDROID
for (int i = 0; i < files.size(); i++)
openPOIFile(files.at(i));
@ -994,7 +1094,7 @@ bool GUI::openPOIFile(const QString &fileName)
return true;
} else {
QString error = tr("Error loading POI file:") + "\n\n"
+ fileName + "\n\n" + _poi->errorString();
+ Util::displayName(fileName) + "\n\n" + _poi->errorString();
if (_poi->errorLine())
error.append("\n" + tr("Line: %1").arg(_poi->errorLine()));
QMessageBox::critical(this, APP_NAME, error);
@ -1207,12 +1307,72 @@ void GUI::exportPNGFile()
void GUI::statistics()
{
QLocale l(QLocale::system());
QMessageBox msgBox(this);
QString text;
#ifdef Q_OS_ANDROID
if (_showTracksAction->isChecked() && _trackCount > 1)
text.append("<b>" + tr("Tracks") + ":</b> "
+ l.toString(_trackCount) + "<br>");
if (_showRoutesAction->isChecked() && _routeCount > 1)
text.append("<b>" + tr("Routes") + ":</b> "
+ l.toString(_routeCount) + "<br>");
if (_showWaypointsAction->isChecked() && _waypointCount > 1)
text.append("<b>" + tr("Waypoints") + ":</b> "
+ l.toString(_waypointCount) + "<br>");
if (_showAreasAction->isChecked() && _areaCount > 1)
text.append("<b>" + tr("Areas") + ":</b> "
+ l.toString(_areaCount) + "<br>");
if (_dateRange.first.isValid()) {
if (_dateRange.first == _dateRange.second) {
QString format = l.dateFormat(QLocale::LongFormat);
text.append("<b>" + tr("Date") + ":</b> "
+ _dateRange.first.toString(format) + "<br>");
} else {
QString format = l.dateFormat(QLocale::ShortFormat);
text.append("<b>" + tr("Date") + ":</b> "
+ QString("%1 - %2").arg(_dateRange.first.toString(format),
_dateRange.second.toString(format)) + "<br>");
}
}
if (distance() > 0)
text.append("<b>" + tr("Distance") + ":</b> "
+ Format::distance(distance(), units()) + "<br>");
if (time() > 0) {
text.append("<b>" + tr("Time") + ":</b> "
+ Format::timeSpan(time()) + "<br>");
text.append("<b>" + tr("Moving time") + ":</b> "
+ Format::timeSpan(movingTime()) + "<br>");
}
text.append("<br>");
for (int i = 0; i < _tabs.count(); i++) {
const GraphTab *tab = _tabs.at(i);
if (tab->isEmpty())
continue;
text.append("<i>" + tab->label() + "</i><br>");
for (int j = 0; j < tab->info().size(); j++) {
const KV<QString, QString> &kv = tab->info().at(j);
text.append("<b>" + kv.key() + ":</b>&nbsp;" + kv.value());
if (j != tab->info().size() - 1)
text.append(" | ");
}
if (i != _tabs.count() - 1)
text.append("<br><br>");
}
msgBox.setWindowTitle(tr("Statistics"));
msgBox.setText(text);
#else // Q_OS_ANDROID
#ifdef Q_OS_WIN32
QString text = "<style>td {white-space: pre; padding-right: 4em;}"
text = "<style>td {white-space: pre; padding-right: 4em;}"
"th {text-align: left; padding-top: 0.5em;}</style><table>";
#else // Q_OS_WIN32
QString text = "<style>td {white-space: pre; padding-right: 2em;}"
text = "<style>td {white-space: pre; padding-right: 2em;}"
"th {text-align: left; padding-top: 0.5em;}</style><table>";
#endif // Q_OS_WIN32
@ -1267,11 +1427,11 @@ void GUI::statistics()
text.append("</table>");
QMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Statistics"));
msgBox.setText("<h3>" + tr("Statistics") + "</h3>");
msgBox.setInformativeText(text);
#endif // Q_OS_ANDROID
msgBox.exec();
}
@ -1430,8 +1590,10 @@ void GUI::reloadFiles()
updateWindowTitle();
if (_files.isEmpty())
_fileActionGroup->setEnabled(false);
#ifndef Q_OS_ANDROID
else
_browser->setCurrent(_files.last());
#endif // Q_OS_ANDROID
updateDEMDownloadAction();
}
@ -1466,6 +1628,10 @@ void GUI::closeAll()
updateWindowTitle();
updateGraphTabs();
updateDEMDownloadAction();
#ifdef Q_OS_ANDROID
_browser->setCurrentDir(QString());
#endif // Q_OS_ANDROID
}
void GUI::showGraphs(bool show)
@ -1473,6 +1639,7 @@ void GUI::showGraphs(bool show)
_graphTabWidget->setHidden(!show);
}
#ifndef Q_OS_ANDROID
void GUI::showToolbars(bool show)
{
if (show) {
@ -1515,6 +1682,7 @@ void GUI::showFullscreen(bool show)
showNormal();
}
}
#endif // Q_OS_ANDROID
void GUI::showTracks(bool show)
{
@ -1583,8 +1751,13 @@ void GUI::showPathMarkerInfo(QAction *action)
void GUI::loadMap()
{
#ifdef Q_OS_ANDROID
QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"),
_mapDir));
#else // Q_OS_ANDROID
QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"),
_mapDir, MapList::formats()));
#endif // Q_OS_ANDROID
MapAction *a, *lastReady = 0;
for (int i = 0; i < files.size(); i++) {
@ -1627,7 +1800,8 @@ bool GUI::loadMapNode(const TreeNode<Map*> &node, MapAction *&action,
if (!map->isValid()) {
if (!silent)
QMessageBox::critical(this, APP_NAME,
tr("Error loading map:") + "\n\n" + map->path() + "\n\n"
tr("Error loading map:") + "\n\n"
+ Util::displayName(map->path()) + "\n\n"
+ map->errorString());
delete map;
} else {
@ -1672,8 +1846,8 @@ void GUI::mapLoaded()
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} else {
QString error = tr("Error loading map:") + "\n\n" + map->path() + "\n\n"
+ map->errorString();
QString error = tr("Error loading map:") + "\n\n"
+ Util::displayName(map->path()) + "\n\n" + map->errorString();
QMessageBox::critical(this, APP_NAME, error);
action->deleteLater();
}
@ -1691,8 +1865,8 @@ void GUI::mapLoadedDir()
actions.append(action);
_mapView->loadMaps(actions);
} else {
QString error = tr("Error loading map:") + "\n\n" + map->path() + "\n\n"
+ map->errorString();
QString error = tr("Error loading map:") + "\n\n"
+ Util::displayName(map->path()) + "\n\n" + map->errorString();
QMessageBox::critical(this, APP_NAME, error);
action->deleteLater();
}
@ -1714,7 +1888,8 @@ void GUI::loadMapDirNode(const TreeNode<Map *> &node, QList<MapAction*> &actions
if (!(a = findMapAction(existingActions, map))) {
if (!map->isValid()) {
QMessageBox::critical(this, APP_NAME, tr("Error loading map:")
+ "\n\n" + map->path() + "\n\n" + map->errorString());
+ "\n\n" + Util::displayName(map->path()) + "\n\n"
+ map->errorString());
delete map;
} else {
a = new MapAction(map, _mapsActionGroup);
@ -1817,7 +1992,7 @@ void GUI::updateStatusBarInfo()
if (_files.count() == 0)
_fileNameLabel->setText(tr("No files loaded"));
else if (_files.count() == 1)
_fileNameLabel->setText(_files.at(0));
_fileNameLabel->setText(Util::displayName(_files.at(0)));
else
_fileNameLabel->setText(tr("%n files", "", _files.count()));
@ -1840,6 +2015,10 @@ void GUI::updateStatusBarInfo()
_timeLabel->clear();
_timeLabel->setToolTip(QString());
}
#ifdef Q_OS_ANDROID
statusBar()->setVisible(!_files.isEmpty());
#endif // Q_OS_ANDROID
}
void GUI::updateWindowTitle()
@ -1929,10 +2108,17 @@ void GUI::graphChanged(int index)
void GUI::updateNavigationActions()
{
#ifdef Q_OS_ANDROID
_navigation->enableNext(!_browser->isLast()
&& !_browser->current().isNull());
_navigation->enablePrev(!_browser->isFirst()
&& !_browser->current().isNull());
#else // Q_OS_ANDROID
_lastAction->setEnabled(!_browser->isLast());
_nextAction->setEnabled(!_browser->isLast());
_firstAction->setEnabled(!_browser->isFirst());
_prevAction->setEnabled(!_browser->isFirst());
#endif // Q_OS_ANDROID
}
bool GUI::updateGraphTabs()
@ -2042,6 +2228,7 @@ void GUI::first()
openFile(file);
}
#ifndef Q_OS_ANDROID
void GUI::keyPressEvent(QKeyEvent *event)
{
QString file;
@ -2096,11 +2283,12 @@ void GUI::keyPressEvent(QKeyEvent *event)
QMainWindow::keyPressEvent(event);
}
#endif // Q_OS_ANDROID
void GUI::closeEvent(QCloseEvent *event)
{
writeSettings();
event->accept();
QMainWindow::closeEvent(event);
}
void GUI::dragEnterEvent(QDragEnterEvent *event)
@ -2167,6 +2355,7 @@ void GUI::writeSettings()
QSettings settings(qApp->applicationName(), qApp->applicationName());
settings.clear();
#ifndef Q_OS_ANDROID
settings.beginGroup(WINDOW_SETTINGS_GROUP);
if (!_windowStates.isEmpty() && !_windowGeometries.isEmpty()) {
settings.setValue(WINDOW_STATE_SETTING, _windowStates.first());
@ -2176,6 +2365,7 @@ void GUI::writeSettings()
settings.setValue(WINDOW_GEOMETRY_SETTING, saveGeometry());
}
settings.endGroup();
#endif // Q_OS_ANDROID
settings.beginGroup(SETTINGS_SETTINGS_GROUP);
if ((_movingTimeAction->isChecked() ? Moving : Total) !=
@ -2190,9 +2380,11 @@ void GUI::writeSettings()
: _degreesMinutesAction->isChecked() ? DegreesMinutes : DecimalDegrees;
if (format != COORDINATES_DEFAULT)
settings.setValue(COORDINATES_SETTING, format);
#ifndef Q_OS_ANDROID
if (_showToolbarsAction->isChecked() != SHOW_TOOLBARS_DEFAULT)
settings.setValue(SHOW_TOOLBARS_SETTING,
_showToolbarsAction->isChecked());
#endif // Q_OS_ANDROID
settings.endGroup();
settings.beginGroup(MAP_SETTINGS_GROUP);
@ -2285,8 +2477,8 @@ void GUI::writeSettings()
settings.setValue(SHOW_TICKS_SETTING,
_showTicksAction->isChecked());
bool sm = _showMarkersAction->isChecked()
| _showMarkerDateAction->isChecked()
| _showMarkerCoordinatesAction->isChecked();
|| _showMarkerDateAction->isChecked()
|| _showMarkerCoordinatesAction->isChecked();
if (sm != SHOW_MARKERS_DEFAULT)
settings.setValue(SHOW_MARKERS_SETTING, sm);
if (_showMarkerDateAction->isChecked()
@ -2485,10 +2677,12 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs)
int value;
QSettings settings(qApp->applicationName(), qApp->applicationName());
#ifndef Q_OS_ANDROID
settings.beginGroup(WINDOW_SETTINGS_GROUP);
restoreGeometry(settings.value(WINDOW_GEOMETRY_SETTING).toByteArray());
restoreState(settings.value(WINDOW_STATE_SETTING).toByteArray());
settings.endGroup();
#endif // Q_OS_ANDROID
settings.beginGroup(SETTINGS_SETTINGS_GROUP);
if (settings.value(TIME_TYPE_SETTING, TIME_TYPE_DEFAULT).toInt() == Moving)
@ -2512,10 +2706,12 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs)
else
_decimalDegreesAction->trigger();
#ifndef Q_OS_ANDROID
if (!settings.value(SHOW_TOOLBARS_SETTING, SHOW_TOOLBARS_DEFAULT).toBool())
showToolbars(false);
else
_showToolbarsAction->setChecked(true);
#endif // Q_OS_ANDROID
settings.endGroup();
settings.beginGroup(MAP_SETTINGS_GROUP);
@ -2523,8 +2719,8 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs)
_showMapAction->setChecked(true);
else
_mapView->showMap(false);
if (settings.value(SHOW_CURSOR_COORDINATES_SETTING, SHOW_CURSOR_COORDINATES_DEFAULT)
.toBool()) {
if (settings.value(SHOW_CURSOR_COORDINATES_SETTING,
SHOW_CURSOR_COORDINATES_DEFAULT).toBool()) {
_showCoordinatesAction->setChecked(true);
_mapView->showCursorCoordinates(true);
}

View File

@ -35,6 +35,7 @@ class MapAction;
class POIAction;
class Data;
class DEMLoader;
class NavigationWidget;
class GUI : public QMainWindow
{
@ -47,15 +48,21 @@ public:
bool loadMap(const QString &fileName, MapAction *&action,
bool silent = false);
void show();
void writeSettings();
private slots:
void about();
#ifndef Q_OS_ANDROID
void keys();
#endif // Q_OS_ANDROID
void paths();
void printFile();
void exportPDFFile();
void exportPNGFile();
void openFile();
#ifdef Q_OS_ANDROID
void openDir();
#endif // Q_OS_ANDROID
void closeAll();
void reloadFiles();
void statistics();
@ -64,8 +71,10 @@ private slots:
void showGraphGrids(bool show);
void showGraphSliderInfo(bool show);
void showPathMarkerInfo(QAction *action);
#ifndef Q_OS_ANDROID
void showToolbars(bool show);
void showFullscreen(bool show);
#endif // Q_OS_ANDROID
void showTracks(bool show);
void showRoutes(bool show);
void showAreas(bool show);
@ -129,7 +138,11 @@ private:
QAction *action = 0);
void createActions();
void createMenus();
#ifdef Q_OS_ANDROID
void createNavigation();
#else // Q_OS_ANDROID
void createToolBars();
#endif // Q_OS_ANDROID
void createStatusBar();
void createMapView();
void createGraphTabs();
@ -160,19 +173,24 @@ private:
QAction *mapAction(const QString &name);
QGeoPositionInfoSource *positionSource(const Options &options);
void readSettings(QString &activeMap, QStringList &disabledPOIs);
void writeSettings();
void loadInitialMaps(const QString &selected);
void loadInitialPOIs(const QStringList &disabled);
#ifndef Q_OS_ANDROID
void keyPressEvent(QKeyEvent *event);
#endif // Q_OS_ANDROID
void closeEvent(QCloseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
#ifdef Q_OS_ANDROID
NavigationWidget *_navigation;
#else // Q_OS_ANDROID
QToolBar *_fileToolBar;
QToolBar *_showToolBar;
QToolBar *_navigationToolBar;
#endif // Q_OS_ANDROID
QMenu *_poiMenu;
QMenu *_mapMenu;
@ -180,15 +198,16 @@ private:
QActionGroup *_navigationActionGroup;
QActionGroup *_mapsActionGroup;
QActionGroup *_poisActionGroup;
#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
QAction *_exitAction;
QAction *_keysAction;
#endif // Q_OS_MAC + Q_OS_ANDROID
QAction *_pathsAction;
QAction *_aboutAction;
QAction *_aboutQtAction;
QAction *_printFileAction;
QAction *_exportPDFFileAction;
QAction *_exportPNGFileAction;
QAction *_openFileAction;
QAction *_openDirAction;
QAction *_closeFileAction;
QAction *_reloadFileAction;
QAction *_statisticsAction;
@ -204,7 +223,6 @@ private:
QAction *_followPositionAction;
QAction *_showPositionCoordinatesAction;
QAction *_showMotionInfo;
QAction *_fullscreenAction;
QAction *_loadMapAction;
QAction *_loadMapDirAction;
QAction *_clearMapCacheAction;
@ -213,11 +231,15 @@ private:
QAction *_showGraphSliderInfoAction;
QAction *_distanceGraphAction;
QAction *_timeGraphAction;
#ifndef Q_OS_ANDROID
QAction *_keysAction;
QAction *_fullscreenAction;
QAction *_showToolbarsAction;
QAction *_nextAction;
QAction *_prevAction;
QAction *_lastAction;
QAction *_firstAction;
#endif // Q_OS_ANDROID
QAction *_metricUnitsAction;
QAction *_imperialUnitsAction;
QAction *_nauticalUnitsAction;
@ -271,9 +293,11 @@ private:
DateTimeRange _dateRange;
QString _pathName;
#ifndef Q_OS_ANDROID
QList<QByteArray> _windowStates;
QList<QByteArray> _windowGeometries;
int _frameStyle;
#endif // Q_OS_ANDROID
PDFExport _pdfExport;
PNGExport _pngExport;

View File

@ -24,10 +24,17 @@ void HeartRateGraph::setInfo()
if (_showTracks) {
QLocale l(QLocale::system());
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale(), 'f', 0)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 0)
+ UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Average"), l.toString(avg() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
} else
clearInfo();
}

View File

@ -662,20 +662,9 @@ void MapView::wheelEvent(QWheelEvent *event)
#else // QT 5.15
zoom((delta > 0) ? 1 : -1, event->position().toPoint(), shift);
#endif // QT 5.15
}
void MapView::mouseDoubleClickEvent(QMouseEvent *event)
{
bool shift = (event->modifiers() & MODIFIER) ? true : false;
QGraphicsView::mouseDoubleClickEvent(event);
if (event->isAccepted())
return;
if (event->button() != Qt::LeftButton && event->button() != Qt::RightButton)
return;
zoom((event->button() == Qt::LeftButton) ? 1 : -1, event->pos(), shift);
/* Do not call QGraphicsView::wheelEvent() here as this would shift the
view ! */
}
void MapView::keyPressEvent(QKeyEvent *event)
@ -713,20 +702,11 @@ void MapView::keyReleaseEvent(QKeyEvent *event)
QGraphicsView::keyReleaseEvent(event);
}
void MapView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && event->modifiers() & MODIFIER)
QApplication::clipboard()->setText(Format::coordinates(
_map->xy2ll(mapToScene(event->pos())), _cursorCoordinates->format()));
else
QGraphicsView::mousePressEvent(event);
}
void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
PlotFlags flags)
{
QRect orig, adj;
qreal ratio, diff, q, p;
qreal mapRatio, ratio, diff, q, p;
QPointF scenePos, scalePos, posPos, motionPos;
int zoom;
@ -734,7 +714,6 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
// Enter plot mode
setUpdatesEnabled(false);
_plot = true;
_map->setDevicePixelRatio(_deviceRatio, 1.0);
// Compute sizes & ratios
orig = viewport()->rect();
@ -753,6 +732,9 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
}
// Expand the view if plotting into a bitmap
mapRatio = _mapRatio;
setDevicePixelRatio(_deviceRatio, 1.0);
if (flags & Expand) {
qreal xdiff = (target.width() - adj.width()) / 2.0;
qreal ydiff = (target.height() - adj.height()) / 2.0;
@ -805,6 +787,9 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
rescale();
centerOn(scenePos);
}
setDevicePixelRatio(_deviceRatio, mapRatio);
_mapScale->setDigitalZoom(_digitalZoom);
_mapScale->setPos(scalePos);
_positionCoordinates->setDigitalZoom(_digitalZoom);
@ -813,7 +798,6 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
_motionInfo->setPos(motionPos);
// Exit plot mode
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
_plot = false;
setUpdatesEnabled(true);
}
@ -1196,6 +1180,12 @@ void MapView::scrollContentsBy(int dx, int dy)
}
}
void MapView::leaveEvent(QEvent *event)
{
_cursorCoordinates->setCoordinates(Coordinates());
QGraphicsView::leaveEvent(event);
}
void MapView::mouseMoveEvent(QMouseEvent *event)
{
if (_cursorCoordinates->isVisible()) {
@ -1206,10 +1196,30 @@ void MapView::mouseMoveEvent(QMouseEvent *event)
QGraphicsView::mouseMoveEvent(event);
}
void MapView::leaveEvent(QEvent *event)
void MapView::mousePressEvent(QMouseEvent *event)
{
_cursorCoordinates->setCoordinates(Coordinates());
QGraphicsView::leaveEvent(event);
if (event->button() == Qt::LeftButton) {
if (event->modifiers() & MODIFIER)
QApplication::clipboard()->setText(Format::coordinates(_map->xy2ll(
mapToScene(event->pos())), _cursorCoordinates->format()));
#ifdef Q_OS_ANDROID
else
emit clicked(event->pos());
#endif // Q_OS_ANDROID
}
QGraphicsView::mousePressEvent(event);
}
void MapView::mouseDoubleClickEvent(QMouseEvent *event)
{
bool shift = (event->modifiers() & MODIFIER) ? true : false;
if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton)
zoom((event->button() == Qt::LeftButton) ? 1 : -1, event->pos(), shift);
QGraphicsView::mouseDoubleClickEvent(event);
}
bool MapView::event(QEvent *event)

View File

@ -102,6 +102,11 @@ public:
RectC boundingRect() const;
#ifdef Q_OS_ANDROID
signals:
void clicked(const QPoint &pos);
#endif // Q_OS_ANDROID
public slots:
void showMap(bool show);
void showPOI(bool show);
@ -162,6 +167,7 @@ private:
void drawBackground(QPainter *painter, const QRectF &rect);
void paintEvent(QPaintEvent *event);
void leaveEvent(QEvent *event);
bool event(QEvent *event);
void scrollContentsBy(int dx, int dy);

View File

@ -0,0 +1,102 @@
#include <QEvent>
#include <QResizeEvent>
#include <QPainter>
#include "mapview.h"
#include "navigationwidget.h"
#define MARGIN 5
#define SIZE 40
#ifdef Q_OS_ANDROID
NavigationWidget::NavigationWidget(MapView *view)
: QWidget(view), _showPrev(false), _showNext(false)
{
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);
newParent();
connect(view, &MapView::clicked, this, &NavigationWidget::viewClicked);
}
bool NavigationWidget::eventFilter(QObject *obj, QEvent *ev)
{
if (obj == parent()) {
if (ev->type() == QEvent::Resize)
resize(static_cast<QResizeEvent*>(ev)->size());
else if (ev->type() == QEvent::ChildAdded)
raise();
}
return QWidget::eventFilter(obj, ev);
}
bool NavigationWidget::event(QEvent* ev)
{
if (ev->type() == QEvent::ParentAboutToChange) {
if (parent())
parent()->removeEventFilter(this);
} else if (ev->type() == QEvent::ParentChange)
newParent();
return QWidget::event(ev);
}
void NavigationWidget::paintEvent(QPaintEvent *ev)
{
Q_UNUSED(ev);
QPainter p(this);
QColor c(Qt::black);
c.setAlpha(64);
p.setBrush(c);
p.setPen(Qt::NoPen);
if (_showPrev) {
QPainterPath path;
path.addEllipse(QRect(MARGIN, rect().center().y() - SIZE/2, SIZE, SIZE));
path.moveTo(QPointF(MARGIN + 0.66*SIZE, rect().center().y() - SIZE/4));
path.lineTo(QPointF(MARGIN + SIZE/4, rect().center().y()));
path.lineTo(QPointF(MARGIN + 0.66*SIZE, rect().center().y() + SIZE/4));
path.closeSubpath();
p.drawPath(path);
}
if (_showNext) {
QPainterPath path;
path.addEllipse(QRect(rect().right() - (MARGIN + SIZE),
rect().center().y() - SIZE/2, SIZE, SIZE));
path.moveTo(QPointF(rect().right() - (MARGIN + 0.66*SIZE),
rect().center().y() - SIZE/4));
path.lineTo(QPointF(rect().right() - (MARGIN + SIZE/4),
rect().center().y()));
path.lineTo(QPointF(rect().right() - (MARGIN + 0.66*SIZE),
rect().center().y() + SIZE/4));
path.closeSubpath();
p.drawPath(path);
}
}
void NavigationWidget::newParent()
{
if (!parent())
return;
parent()->installEventFilter(this);
raise();
}
void NavigationWidget::viewClicked(const QPoint &pos)
{
QRect prevRect(MARGIN, rect().center().y() - SIZE/2, SIZE, SIZE);
QRect nextRect(rect().right() - (MARGIN + SIZE), rect().center().y()
- SIZE/2, SIZE, SIZE);
if (prevRect.contains(pos))
emit prev();
else if (nextRect.contains(pos))
emit next();
}
#endif // Q_OS_ANDROID

View File

@ -0,0 +1,36 @@
#ifndef NAVIGATIONWIDGET_H
#define NAVIGATIONWIDGET_H
#include <QWidget>
class MapView;
#ifdef Q_OS_ANDROID
class NavigationWidget : public QWidget
{
Q_OBJECT
public:
NavigationWidget(MapView *view);
void enableNext(bool enable) {_showNext = enable; update();}
void enablePrev(bool enable) {_showPrev = enable; update();}
signals:
void next();
void prev();
private slots:
void viewClicked(const QPoint &pos);
private:
bool eventFilter(QObject *obj, QEvent *ev);
bool event(QEvent *ev);
void paintEvent(QPaintEvent *ev);
void newParent();
bool _showPrev, _showNext;
};
#endif // Q_OS_ANDROID
#endif // NAVIGATIONWIDGET_H

View File

@ -26,8 +26,11 @@
#include "pluginparameters.h"
#include "optionsdialog.h"
#ifdef Q_OS_ANDROID
#define MENU_MARGIN 0
#else // Q_OS_ANDROID
#define MENU_MARGIN 20
#endif // Q_OS_ANDROID
#define MENU_ICON_SIZE 32
#ifdef Q_OS_MAC
@ -714,7 +717,7 @@ QWidget *OptionsDialog::createSystemPage()
_pixmapCache = new QSpinBox();
_pixmapCache->setMinimum(16);
_pixmapCache->setMaximum(1024);
_pixmapCache->setMaximum(2048);
_pixmapCache->setSuffix(UNIT_SPACE + tr("MB"));
_pixmapCache->setValue(_options.pixmapCache);
@ -779,6 +782,11 @@ QWidget *OptionsDialog::createSystemPage()
OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
: QDialog(parent), _options(options), _units(units)
{
#ifdef Q_OS_ANDROID
setWindowFlags(Qt::Window);
setWindowState(Qt::WindowFullScreen);
#endif /* Q_OS_ANDROID */
QStackedWidget *pages = new QStackedWidget();
pages->addWidget(createAppearancePage());
pages->addWidget(createMapPage());
@ -791,16 +799,25 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
QListWidget *menu = new QListWidget();
menu->setIconSize(QSize(MENU_ICON_SIZE, MENU_ICON_SIZE));
new QListWidgetItem(QIcon(APPEARANCE_ICON), tr("Appearance"),
menu);
#ifdef Q_OS_ANDROID
new QListWidgetItem(QIcon(APPEARANCE_ICON), QString(), menu);
new QListWidgetItem(QIcon(MAPS_ICON), QString(), menu);
new QListWidgetItem(QIcon(DATA_ICON), QString(), menu);
new QListWidgetItem(QIcon(POI_ICON), QString(), menu);
new QListWidgetItem(QIcon(DEM_ICON), QString(), menu);
new QListWidgetItem(QIcon(POSITION_ICON), QString(), menu);
new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), QString(), menu);
new QListWidgetItem(QIcon(SYSTEM_ICON), QString(), menu);
#else // Q_OS_ANDROID
new QListWidgetItem(QIcon(APPEARANCE_ICON), tr("Appearance"), menu);
new QListWidgetItem(QIcon(MAPS_ICON), tr("Maps"), menu);
new QListWidgetItem(QIcon(DATA_ICON), tr("Data"), menu);
new QListWidgetItem(QIcon(POI_ICON), tr("POI"), menu);
new QListWidgetItem(QIcon(DEM_ICON), tr("DEM"), menu);
new QListWidgetItem(QIcon(POSITION_ICON), tr("Position"), menu);
new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"),
menu);
new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"), menu);
new QListWidgetItem(QIcon(SYSTEM_ICON), tr("System"), menu);
#endif // Q_OS_ANDROID
QHBoxLayout *contentLayout = new QHBoxLayout();
contentLayout->addWidget(menu);

View File

@ -18,9 +18,16 @@ PDFExportDialog::PDFExportDialog(PDFExport &exp, Units units, QWidget *parent)
{
int index;
#ifdef Q_OS_ANDROID
setWindowFlags(Qt::Window);
setWindowState(Qt::WindowFullScreen);
#endif /* Q_OS_ANDROID */
_fileSelect = new FileSelectWidget();
#ifndef Q_OS_ANDROID
_fileSelect->setFilter(tr("PDF files") + " (*.pdf);;" + tr("All files")
+ " (*)");
#endif // Q_OS_ANDROID
_fileSelect->setFile(_export.fileName);
_paperSize = new QComboBox();
@ -102,6 +109,9 @@ PDFExportDialog::PDFExportDialog(PDFExport &exp, Units units, QWidget *parent)
#else // Q_OS_MAC
layout->addWidget(pageSetupBox);
layout->addWidget(outputFileBox);
#ifdef Q_OS_ANDROID
layout->addStretch();
#endif // Q_OS_ANDROID
#endif // Q_OS_MAC
layout->addWidget(buttonBox);
setLayout(layout);

View File

@ -15,9 +15,16 @@
PNGExportDialog::PNGExportDialog(PNGExport &exp, QWidget *parent)
: QDialog(parent), _export(exp)
{
#ifdef Q_OS_ANDROID
setWindowFlags(Qt::Window);
setWindowState(Qt::WindowFullScreen);
#endif /* Q_OS_ANDROID */
_fileSelect = new FileSelectWidget();
#ifndef Q_OS_ANDROID
_fileSelect->setFilter(tr("PNG files") + " (*.png);;" + tr("All files")
+ " (*)");
#endif // Q_OS_ANDROID
_fileSelect->setFile(_export.fileName);
_width = new QSpinBox();
@ -78,6 +85,9 @@ PNGExportDialog::PNGExportDialog(PNGExport &exp, QWidget *parent)
#else // Q_OS_MAC
layout->addWidget(pageSetupBox);
layout->addWidget(outputFileBox);
#ifdef Q_OS_ANDROID
layout->addStretch();
#endif // Q_OS_ANDROID
#endif // Q_OS_MAC
layout->addWidget(buttonBox);
setLayout(layout);

View File

@ -24,10 +24,17 @@ void PowerGraph::setInfo()
if (_showTracks) {
QLocale l(QLocale::system());
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Average"), l.toString(avg() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
} else
clearInfo();
}

View File

@ -1,6 +1,8 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#include <QtGlobal>
#define IMPERIAL_UNITS() \
(QLocale::system().measurementSystem() == QLocale::ImperialSystem)
#define POSITION_PLUGIN() \
@ -215,7 +217,11 @@
#define ENABLE_HTTP2_SETTING "enableHTTP2"
#define ENABLE_HTTP2_DEFAULT true
#define PIXMAP_CACHE_SETTING "pixmapCache"
#ifdef Q_OS_ANDROID
#define PIXMAP_CACHE_DEFAULT 256 /* MB */
#else // Q_OS_ANDROID
#define PIXMAP_CACHE_DEFAULT 512 /* MB */
#endif // Q_OS_ANDROID
#define CONNECTION_TIMEOUT_SETTING "connectionTimeout"
#define CONNECTION_TIMEOUT_DEFAULT 30 /* s */
#define HIRES_PRINT_SETTING "hiresPrint"

View File

@ -31,10 +31,17 @@ void SpeedGraph::setInfo()
QString pu = (_units == Metric) ? tr("min/km") : (_units == Imperial) ?
tr("min/mi") : tr("min/nmi");
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale(), 'f', 1)
+ UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 1)
+ UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Average"), l.toString(avg() * yScale(), 'f',
1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale(), 'f',
1) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
GraphView::addInfo(tr("Pace"), pace + UNIT_SPACE + pu);
} else
clearInfo();

View File

@ -24,12 +24,21 @@ void TemperatureGraph::setInfo()
if (_showTracks) {
QLocale l(QLocale::system());
#ifdef Q_OS_ANDROID
GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Min"), l.toString(min() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Max"), l.toString(max() * yScale() + yOffset(),
'f', 1) + UNIT_SPACE + yUnits());
#else // Q_OS_ANDROID
GraphView::addInfo(tr("Average"), l.toString(avg() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Minimum"), l.toString(min() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
#endif // Q_OS_ANDROID
} else
clearInfo();
}

View File

@ -32,11 +32,21 @@ Thumbnail::Thumbnail(const QString &path, int limit, QWidget *parent)
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
#ifdef Q_OS_ANDROID
_path = path;
#else //Q_OS_ANDROID
_path = QFileInfo(path).absoluteFilePath();
#endif // Q_OS_ANDROID
}
void Thumbnail::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
#ifdef Q_OS_ANDROID
QDesktopServices::openUrl(_path);
#else // Q_OS_ANDROID
QDesktopServices::openUrl(QUrl::fromLocalFile(_path));
#endif // Q_OS_ANDROID
QLabel::mousePressEvent(event);
}

View File

@ -19,65 +19,97 @@
#define TYP_FILE "style.typ"
#define RENDERTHEME_FILE "style.xml"
#ifdef Q_OS_ANDROID
#define DATA_LOCATION QStandardPaths::GenericDataLocation
#else // Q_OS_ANDROID
#define DATA_LOCATION QStandardPaths::AppDataLocation
#endif // Q_OS_ANDROID
#ifdef Q_OS_ANDROID
static QString assetsPath(const QString &path, const QString &dir)
{
QDir pd(path);
if (pd.isAbsolute() && pd.exists())
return pd.absolutePath();
else
return QString("assets://") + dir;
}
#endif // Q_OS_ANDROID
QString ProgramPaths::mapDir(bool writable)
{
if (writable)
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation)).filePath(MAP_DIR);
return QDir(QStandardPaths::writableLocation(DATA_LOCATION))
.filePath(MAP_DIR);
else
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
#ifdef Q_OS_ANDROID
return assetsPath(QStandardPaths::locate(DATA_LOCATION, MAP_DIR,
QStandardPaths::LocateDirectory), MAP_DIR);
#else // Q_OS_ANDROID
return QStandardPaths::locate(DATA_LOCATION,
MAP_DIR, QStandardPaths::LocateDirectory);
#endif // Q_OS_ANDROID
}
QString ProgramPaths::poiDir(bool writable)
{
if (writable)
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation)).filePath(POI_DIR);
return QDir(QStandardPaths::writableLocation(DATA_LOCATION))
.filePath(POI_DIR);
else
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
POI_DIR, QStandardPaths::LocateDirectory);
return QStandardPaths::locate(DATA_LOCATION, POI_DIR,
QStandardPaths::LocateDirectory);
}
QString ProgramPaths::csvDir(bool writable)
{
if (writable)
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation)).filePath(CSV_DIR);
return QDir(QStandardPaths::writableLocation(DATA_LOCATION))
.filePath(CSV_DIR);
else
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
CSV_DIR, QStandardPaths::LocateDirectory);
#ifdef Q_OS_ANDROID
return assetsPath(QStandardPaths::locate(DATA_LOCATION, CSV_DIR,
QStandardPaths::LocateDirectory), CSV_DIR);
#else // Q_OS_ANDROID
return QStandardPaths::locate(DATA_LOCATION, CSV_DIR,
QStandardPaths::LocateDirectory);
#endif // Q_OS_ANDROID
}
QString ProgramPaths::demDir(bool writable)
{
if (writable)
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation)).filePath(DEM_DIR);
return QDir(QStandardPaths::writableLocation(DATA_LOCATION))
.filePath(DEM_DIR);
else
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
DEM_DIR, QStandardPaths::LocateDirectory);
return QStandardPaths::locate(DATA_LOCATION, DEM_DIR,
QStandardPaths::LocateDirectory);
}
QString ProgramPaths::styleDir(bool writable)
{
if (writable)
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation)).filePath(STYLE_DIR);
return QDir(QStandardPaths::writableLocation(DATA_LOCATION))
.filePath(STYLE_DIR);
else
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
STYLE_DIR, QStandardPaths::LocateDirectory);
return QStandardPaths::locate(DATA_LOCATION, STYLE_DIR,
QStandardPaths::LocateDirectory);
}
QString ProgramPaths::symbolsDir(bool writable)
{
if (writable)
return QDir(QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation)).filePath(SYMBOLS_DIR);
return QDir(QStandardPaths::writableLocation(DATA_LOCATION))
.filePath(SYMBOLS_DIR);
else
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
SYMBOLS_DIR, QStandardPaths::LocateDirectory);
#ifdef Q_OS_ANDROID
return assetsPath(QStandardPaths::locate(DATA_LOCATION, SYMBOLS_DIR,
QStandardPaths::LocateDirectory), SYMBOLS_DIR);
#else // Q_OS_ANDROID
return QStandardPaths::locate(DATA_LOCATION, SYMBOLS_DIR,
QStandardPaths::LocateDirectory);
#endif // Q_OS_ANDROID
}
QString ProgramPaths::tilesDir()
@ -88,36 +120,31 @@ QString ProgramPaths::tilesDir()
QString ProgramPaths::translationsDir()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
TRANSLATIONS_DIR, QStandardPaths::LocateDirectory);
return QStandardPaths::locate(DATA_LOCATION, TRANSLATIONS_DIR,
QStandardPaths::LocateDirectory);
}
QString ProgramPaths::ellipsoidsFile()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
CSV_DIR "/" ELLIPSOID_FILE, QStandardPaths::LocateFile);
return QDir(csvDir()).filePath(ELLIPSOID_FILE);
}
QString ProgramPaths::gcsFile()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
CSV_DIR "/" GCS_FILE, QStandardPaths::LocateFile);
return QDir(csvDir()).filePath(GCS_FILE);
}
QString ProgramPaths::pcsFile()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
CSV_DIR "/" PCS_FILE, QStandardPaths::LocateFile);
return QDir(csvDir()).filePath(PCS_FILE);
}
QString ProgramPaths::typFile()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
STYLE_DIR "/" TYP_FILE, QStandardPaths::LocateFile);
return QDir(styleDir()).filePath(TYP_FILE);
}
QString ProgramPaths::renderthemeFile()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
STYLE_DIR "/" RENDERTHEME_FILE, QStandardPaths::LocateFile);
return QDir(styleDir()).filePath(RENDERTHEME_FILE);
}

View File

@ -1,9 +1,71 @@
#include <cctype>
#include <cmath>
#include <QFileInfo>
#ifdef Q_OS_ANDROID
#include <QUrl>
#include <QCoreApplication>
#include <QJniEnvironment>
#include <QJniObject>
#endif // Q_OS_ANDROID
#include "util.h"
#ifdef Q_OS_ANDROID
static QString documentName(const QString &path)
{
QJniEnvironment env;
QJniObject urlString = QJniObject::fromString(path);
QJniObject uri = QJniObject::callStaticObjectMethod("android/net/Uri",
"parse", "(Ljava/lang/String;)Landroid/net/Uri;",
urlString.object<jstring>());
if (!uri.isValid()) {
env->ExceptionClear();
return QString();
}
QJniObject context = QNativeInterface::QAndroidApplication::context();
if (!context.isValid()) {
env->ExceptionClear();
return QString();
}
QJniObject contentResolver = context.callObjectMethod(
"getContentResolver", "()Landroid/content/ContentResolver;");
if (!contentResolver.isValid()) {
env->ExceptionClear();
return QString();
}
QJniObject columnName = QJniObject::getStaticObjectField<jstring>(
"android/provider/MediaStore$MediaColumns", "DISPLAY_NAME");
if (!columnName.isValid()) {
env->ExceptionClear();
return QString();
}
jobjectArray stringArray = env->NewObjectArray(
1, env->FindClass("java/lang/String"), 0);
env->SetObjectArrayElement(stringArray, 0, columnName.object<jstring>());
QJniObject cursor = contentResolver.callObjectMethod("query",
"(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;"
"Landroid/os/CancellationSignal;)Landroid/database/Cursor;",
uri.object(), stringArray, 0, 0);
if (!cursor.isValid()) {
env->ExceptionClear();
return QString();
}
if (!cursor.callMethod<jboolean>("moveToFirst")) {
env->ExceptionClear();
return QString();
}
QJniObject str = cursor.callObjectMethod("getString",
"(I)Ljava/lang/String;", 0);
if (!str.isValid()) {
env->ExceptionClear();
return QString();
}
return str.toString();
}
#endif // Q_OS_ANDROID
int Util::str2int(const char *str, int len)
{
int res = 0;
@ -52,6 +114,28 @@ double Util::niceNum(double x, bool round)
QString Util::file2name(const QString &path)
{
QFileInfo fi(path);
QFileInfo fi(displayName(path));
return fi.baseName().replace('_', ' ');
}
QString Util::displayName(const QString &path)
{
#ifdef Q_OS_ANDROID
QUrl url(path);
// Not an Android URL, return standard filename.
if (url.scheme() != "content") {
QFileInfo fi(path);
return fi.fileName();
// Directory browsing URLs. Those can not be translated using the Android
// content resolver but we can get the filename from the URL path.
} else if (url.path().startsWith("/tree/")) {
QFileInfo fi(url.fileName());
return fi.fileName();
// Translate all "regular" android URLs using the Android content resolver.
} else
return documentName(path);
#else
return path;
#endif // Q_OS_ANDROID
}

View File

@ -8,6 +8,7 @@ namespace Util
int str2int(const char *str, int len);
double niceNum(double x, bool round);
QString file2name(const QString &path);
QString displayName(const QString &path);
}
#endif // UTIL_H

View File

@ -174,6 +174,8 @@ void RasterTile::render()
qDeleteAll(textItems);
_valid = true;
//painter.setPen(Qt::red);
//painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(QRect(_xy, _pixmap.size()));
@ -185,7 +187,7 @@ void RasterTile::ll2xy(QList<MapData::Poly> &polys)
MapData::Poly &poly = polys[i];
for (int j = 0; j < poly.points.size(); j++) {
QPointF &p = poly.points[j];
p = _map->ll2xy(Coordinates(p.x(), p.y()));
p = ll2xy(Coordinates(p.x(), p.y()));
}
}
}
@ -193,7 +195,7 @@ void RasterTile::ll2xy(QList<MapData::Poly> &polys)
void RasterTile::ll2xy(QList<MapData::Point> &points)
{
for (int i = 0; i < points.size(); i++) {
QPointF p(_map->ll2xy(points.at(i).coordinates));
QPointF p(ll2xy(points.at(i).coordinates));
points[i].coordinates = Coordinates(p.x(), p.y());
}
}
@ -210,8 +212,8 @@ void RasterTile::drawPolygons(QPainter *painter)
if (poly.raster.isValid()) {
RectC r(poly.raster.rect());
QPointF tl(_map->ll2xy(r.topLeft()));
QPointF br(_map->ll2xy(r.bottomRight()));
QPointF tl(ll2xy(r.topLeft()));
QPointF br(ll2xy(r.bottomRight()));
QSizeF size(QRectF(tl, br).size());
bool insert = false;

View File

@ -3,6 +3,8 @@
#include <QPixmap>
#include "mapdata.h"
#include "map/projection.h"
#include "map/transform.h"
class QPainter;
class IMGMap;
@ -15,20 +17,25 @@ class Style;
class RasterTile
{
public:
RasterTile(IMGMap *map, const Style *style, int zoom, const QRect &rect,
qreal ratio, const QString &key, const QList<MapData::Poly> &polygons,
RasterTile(const Projection &proj, const Transform &transform,
const Style *style, int zoom, const QRect &rect, qreal ratio,
const QString &key, const QList<MapData::Poly> &polygons,
const QList<MapData::Poly> &lines, QList<MapData::Point> &points)
: _map(map), _style(style), _zoom(zoom), _rect(rect), _ratio(ratio),
_key(key), _pixmap(rect.width() * ratio, rect.height() * ratio),
_polygons(polygons), _lines(lines), _points(points) {}
: _proj(proj), _transform(transform), _style(style), _zoom(zoom),
_rect(rect), _ratio(ratio), _key(key),
_pixmap(rect.width() * ratio, rect.height() * ratio), _polygons(polygons),
_lines(lines), _points(points), _valid(false) {}
const QString &key() const {return _key;}
QPoint xy() const {return _rect.topLeft();}
const QPixmap &pixmap() const {return _pixmap;}
bool isValid() const {return _valid;}
void render();
private:
QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));}
void ll2xy(QList<MapData::Poly> &polys);
void ll2xy(QList<MapData::Point> &points);
@ -42,7 +49,8 @@ private:
void processShields(QList<TextItem*> &textItems);
void processStreetNames(QList<TextItem*> &textItems);
IMGMap *_map;
Projection _proj;
Transform _transform;
const Style *_style;
int _zoom;
QRect _rect;
@ -52,6 +60,7 @@ private:
QList<MapData::Poly> _polygons;
QList<MapData::Poly> _lines;
QList<MapData::Point> _points;
bool _valid;
};
}

View File

@ -71,6 +71,8 @@ void IMGMap::load()
void IMGMap::unload()
{
cancelJobs(true);
for (int i = 0; i < _data.size(); i++)
_data.at(i)->clear();
}
@ -100,6 +102,8 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
int IMGMap::zoomIn()
{
cancelJobs(false);
_zoom = qMin(_zoom + 1, _data.first()->zooms().max());
updateTransform();
return _zoom;
@ -107,6 +111,8 @@ int IMGMap::zoomIn()
int IMGMap::zoomOut()
{
cancelJobs(false);
_zoom = qMax(_zoom - 1, _data.first()->zooms().min());
updateTransform();
return _zoom;
@ -139,10 +145,55 @@ void IMGMap::updateTransform()
_bounds.adjust(0.5, 0, -0.5, 0);
}
bool IMGMap::isRunning(const QString &key) const
{
for (int i = 0; i < _jobs.size(); i++) {
const QList<IMG::RasterTile> &tiles = _jobs.at(i)->tiles();
for (int j = 0; j < tiles.size(); j++)
if (tiles.at(j).key() == key)
return true;
}
return false;
}
void IMGMap::runJob(IMGMapJob *job)
{
_jobs.append(job);
connect(job, &IMGMapJob::finished, this, &IMGMap::jobFinished);
job->run();
}
void IMGMap::removeJob(IMGMapJob *job)
{
_jobs.removeOne(job);
job->deleteLater();
}
void IMGMap::jobFinished(IMGMapJob *job)
{
const QList<IMG::RasterTile> &tiles = job->tiles();
for (int i = 0; i < tiles.size(); i++) {
const IMG::RasterTile &mt = tiles.at(i);
if (mt.isValid())
QPixmapCache::insert(mt.key(), mt.pixmap());
}
removeJob(job);
emit tilesLoaded();
}
void IMGMap::cancelJobs(bool wait)
{
for (int i = 0; i < _jobs.size(); i++)
_jobs.at(i)->cancel(wait);
}
void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
Q_UNUSED(flags);
QPointF tl(floor(rect.left() / TILE_SIZE)
* TILE_SIZE, floor(rect.top() / TILE_SIZE) * TILE_SIZE);
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
@ -159,6 +210,9 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QString key(_data.at(n)->fileName() + "-" + QString::number(_zoom)
+ "_" + QString::number(ttl.x()) + "_" + QString::number(ttl.y()));
if (isRunning(key))
continue;
if (QPixmapCache::find(key, &pm))
painter->drawPixmap(ttl, pm);
else {
@ -182,7 +236,8 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
_data.at(n)->points(pointRectD.toRectC(_projection, 20),
_zoom, &points);
tiles.append(RasterTile(this, _data.at(n)->style(), _zoom,
tiles.append(RasterTile(_projection, _transform,
_data.at(n)->style(), _zoom,
QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio, key,
polygons, lines, points));
}
@ -190,14 +245,19 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
}
}
QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
future.waitForFinished();
if (!tiles.isEmpty()) {
if (flags & Map::Block) {
QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
future.waitForFinished();
for (int i = 0; i < tiles.size(); i++) {
const RasterTile &mt = tiles.at(i);
const QPixmap &pm = mt.pixmap();
painter->drawPixmap(mt.xy(), pm);
QPixmapCache::insert(mt.key(), pm);
for (int i = 0; i < tiles.size(); i++) {
const RasterTile &mt = tiles.at(i);
const QPixmap &pm = mt.pixmap();
painter->drawPixmap(mt.xy(), pm);
QPixmapCache::insert(mt.key(), pm);
}
} else
runJob(new IMGMapJob(tiles));
}
}

View File

@ -1,12 +1,49 @@
#ifndef IMGMAP_H
#define IMGMAP_H
#include <QtConcurrent>
#include "map.h"
#include "projection.h"
#include "transform.h"
#include "IMG/mapdata.h"
#include "IMG/rastertile.h"
class IMGMapJob : public QObject
{
Q_OBJECT
public:
IMGMapJob(const QList<IMG::RasterTile> &tiles)
: _tiles(tiles) {}
void run()
{
connect(&_watcher, &QFutureWatcher<void>::finished, this,
&IMGMapJob::handleFinished);
_future = QtConcurrent::map(_tiles, &IMG::RasterTile::render);
_watcher.setFuture(_future);
}
void cancel(bool wait)
{
_future.cancel();
if (wait)
_future.waitForFinished();
}
const QList<IMG::RasterTile> &tiles() const {return _tiles;}
signals:
void finished(IMGMapJob *job);
private slots:
void handleFinished() {emit finished(this);}
private:
QFutureWatcher<void> _watcher;
QFuture<void> _future;
QList<IMG::RasterTile> _tiles;
};
class IMGMap : public Map
{
Q_OBJECT
@ -44,9 +81,16 @@ public:
static Map* create(const QString &path, const Projection &, bool *isDir);
private slots:
void jobFinished(IMGMapJob *job);
private:
Transform transform(int zoom) const;
void updateTransform();
bool isRunning(const QString &key) const;
void runJob(IMGMapJob *job);
void removeJob(IMGMapJob *job);
void cancelJobs(bool wait);
QList<IMG::MapData *> _data;
int _zoom;
@ -56,6 +100,8 @@ private:
RectC _dataBounds;
qreal _tileRatio;
QList<IMGMapJob*> _jobs;
bool _valid;
QString _errorString;
};

View File

@ -1,4 +1,5 @@
#include <QPainter>
#include <QPixmapCache>
#include "common/wgs84.h"
#include "pcs.h"
#include "rectd.h"
@ -164,8 +165,6 @@ void MapsforgeMap::cancelJobs()
void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
Q_UNUSED(flags);
QPointF tl(floor(rect.left() / _data.tileSize()) * _data.tileSize(),
floor(rect.top() / _data.tileSize()) * _data.tileSize());
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
@ -215,8 +214,20 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
}
}
if (!tiles.isEmpty())
runJob(new MapsforgeMapJob(tiles));
if (!tiles.isEmpty()) {
if (flags & Map::Block) {
QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
future.waitForFinished();
for (int i = 0; i < tiles.size(); i++) {
const RasterTile &mt = tiles.at(i);
const QPixmap &pm = mt.pixmap();
painter->drawPixmap(mt.xy(), pm);
QPixmapCache::insert(key(mt.zoom(), mt.xy()), pm);
}
} else
runJob(new MapsforgeMapJob(tiles));
}
}
void MapsforgeMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)

View File

@ -2,7 +2,6 @@
#define MAPSFORGEMAP_H
#include <QtConcurrent>
#include <QPixmapCache>
#include "mapsforge/mapdata.h"
#include "mapsforge/rastertile.h"
#include "projection.h"