diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml new file mode 100644 index 000000000..f9dc9f769 --- /dev/null +++ b/.github/workflows/ios.yml @@ -0,0 +1,77 @@ +name: Compile on iOS + +on: + push: + branches: [ feature/cleanup ] + +jobs: + build: + + runs-on: macos-latest + + steps: + - name: Checkout Enroute + uses: actions/checkout@v4 + with: + path: enroute + submodules: recursive + - name: Checkout Enroute Dependencies + uses: actions/checkout@v4 + with: + path: enrouteDependencies + repository: Akaflieg-Freiburg/enrouteDependencies + submodules: recursive + - name: Set up macOS + uses: actions/setup-python@v4 + with: + python-version: '3.8' + - name: Install Ninja + run: brew install ninja + - name: Install Qt for Desktop + uses: jurplel/install-qt-action@v3 + with: + cache: 'true' + cache-key-prefix: 'install-qt-action-macOS-host' + version: '6.6.*' + modules: 'qt5compat qthttpserver qtimageformats qtlocation qtmultimedia qtpositioning qtshadertools qtspeech qtwebsockets qtwebview' + - name: Set QT_HOST_PATH + run: | + echo QT_HOST_PATH=$Qt6_DIR >> $GITHUB_ENV + - name: Install Qt for iOS + uses: jurplel/install-qt-action@v3 + with: + cache: 'true' + cache-key-prefix: 'install-qt-action-iOS' + target: 'ios' + version: '6.6.*' + modules: 'qt5compat qthttpserver qtimageformats qtlocation qtmultimedia qtpositioning qtshadertools qtsensors qtspeech qtwebsockets qtwebview' + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: macos + - name: Compile EnrouteDependencies + run: | + Qt6_DIR_BASE=$(dirname "$Qt6_DIR") ./buildscript-ios.sh + cp -prv Qt/* $Qt6_DIR/../.. + working-directory: enrouteDependencies + - name: Compile Enroute Flight Navigation + run: | + $Qt6_DIR/bin/qt-cmake \ + -B build-enroute \ + -S enroute \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_C_COMPILER_LAUNCHER="ccache" \ + -DCMAKE_CXX_COMPILER_LAUNCHER="ccache" \ + -DCMAKE_INSTALL_PREFIX=. \ + -- -sdk iphonesimulator + cmake --build build-enroute + - name: Package + run: | + brew install create-dmg + create-dmg enroute-iphonesimulator.dmg build-enroute/src/Debug-iphonesimulator + - name: Upload to developerBuilds + run: | + gh release upload --clobber developerBuilds ../enroute-iphonesimulator.dmg + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: enroute diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 8f6be17cd..511c85ad6 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -2,7 +2,7 @@ name: Compile on MacOS on: push: - branches: [ feature/cleanup ] + branches: [ develop ] jobs: build: @@ -30,6 +30,7 @@ jobs: - name: Install Qt uses: jurplel/install-qt-action@v3 with: + cache: 'true' version: '6.6.*' modules: 'qt5compat qthttpserver qtimageformats qtlocation qtmultimedia qtpositioning qtshadertools qtspeech qtwebsockets qtwebview' - name: ccache @@ -49,6 +50,17 @@ jobs: -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_C_COMPILER_LAUNCHER="ccache" \ -DCMAKE_CXX_COMPILER_LAUNCHER="ccache" \ - -DCMAKE_INSTALL_PREFIX:STRING=$PWD/app/usr + -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" \ + -DCMAKE_INSTALL_PREFIX=. cmake --build build-enroute + - name: Package + run: | cmake --build build-enroute --target install + $Qt6_DIR/bin/macdeployqt enroute.app -dmg + mv enroute.dmg enroute-macOS-universal.dmg + - name: Upload to developerBuilds + run: | + gh release upload --clobber developerBuilds ../enroute-macOS-universal.dmg + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: enroute diff --git a/3rdParty/enrouteManual b/3rdParty/enrouteManual index 700b7a3e6..18eb0ae13 160000 --- a/3rdParty/enrouteManual +++ b/3rdParty/enrouteManual @@ -1 +1 @@ -Subproject commit 700b7a3e6088b615165e470eef42d01b340b8470 +Subproject commit 18eb0ae137a85c6d1cceee66f2861c61750a574b diff --git a/3rdParty/enrouteText b/3rdParty/enrouteText index ac6053e48..90dc76963 160000 --- a/3rdParty/enrouteText +++ b/3rdParty/enrouteText @@ -1 +1 @@ -Subproject commit ac6053e484f7dbf1d90d5f7b326a0ba86745a4b3 +Subproject commit 90dc76963f5aeb57833be0b0e74f8eac34086baf diff --git a/CMakeLists.txt b/CMakeLists.txt index e603d571a..53e190f67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,28 @@ # # Set up cmake # + set(CMAKE_OSX_DEPLOYMENT_TARGET "14.0") cmake_minimum_required(VERSION 3.16) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) - include(ExternalProject) option(BUILD_DOC "Build developer documentation" OFF) - - # # Project data # -project(enroute VERSION 2.30.9) +project(enroute VERSION 2.30.10) set(APP_ID de.akaflieg_freiburg.enroute) set(DISPLAY_NAME "Enroute") math(EXPR PROJECT_VERSION_CODE 10000*${PROJECT_VERSION_MAJOR}+100*${PROJECT_VERSION_MINOR}+${PROJECT_VERSION_PATCH}) add_compile_definitions(PROJECT_VERSION="${PROJECT_VERSION}") message("CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}") + # # Installation # @@ -35,12 +34,28 @@ include(GNUInstallDirs) # Qt Setup # +find_package(Qt6 6.6.2 COMPONENTS Concurrent Core Core5Compat Gui HttpServer LinguistTools Location Positioning Quick QuickControls2 QuickWidgets Sql Svg TextToSpeech Widgets REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "Android") + find_package(Qt6 COMPONENTS Sensors WebView REQUIRED) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + find_package(Qt6 COMPONENTS WebView REQUIRED) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + find_package(Qt6 COMPONENTS Sensors WebView REQUIRED) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_package(Qt6 COMPONENTS DBus Widgets REQUIRED) +endif() + set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) - -find_package(Qt6 6.6.2 COMPONENTS Concurrent Core Core5Compat Gui HttpServer LinguistTools Location Positioning Quick QuickControls2 QuickWidgets Sql Svg TextToSpeech WebView Widgets REQUIRED) qt_policy(SET QTP0002 NEW) @@ -53,21 +68,13 @@ find_package(libzip REQUIRED) find_package(Git REQUIRED) find_package(QMapLibre COMPONENTS Location REQUIRED) - -if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_package(Qt6 COMPONENTS DBus WebView Widgets REQUIRED) -endif() - -if( ANDROID OR IOS) - find_package(Qt6 COMPONENTS Sensors WebView REQUIRED) -endif() - if(IOS) set(PRODUCT_BUNDLE_IDENTIFIER de.akafliegfreiburg.enroute) set(CMAKE_XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon) include(CMakeLists_ios.txt OPTIONAL) endif() + # # Create target collecting all documentation # diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a134d5d8..0c3f70d5e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -316,7 +316,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") Qt6::Sql Qt6::Svg Qt6::TextToSpeech - Qt6::WebView Qt6::Widgets QMapLibre::Location libzip::zip diff --git a/src/main.cpp b/src/main.cpp index 63d1a934c..958a387f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,7 +97,7 @@ auto main(int argc, char *argv[]) -> int QGuiApplication::setWindowIcon(QIcon(u":/icons/appIcon.png"_qs)); // Install translators - QString preferredLanguage = GlobalObject::platformAdaptor()->language(); + QString const preferredLanguage = GlobalObject::platformAdaptor()->language(); auto* enrouteTranslator = new QTranslator(&app); if (enrouteTranslator->load(QStringLiteral(":i18n/enroute_%1.qm").arg(preferredLanguage))) { @@ -139,18 +139,25 @@ auto main(int argc, char *argv[]) -> int parser.setApplicationDescription(QCoreApplication::translate("main", "Enroute Flight Navigation is a free nagivation app for VFR pilots,\ndeveloped as a project of Akaflieg Freiburg.")); parser.addHelpOption(); parser.addVersionOption(); - QCommandLineOption googlePlayScreenshotOption(QStringLiteral("sg"), - QCoreApplication::translate("main", "Run simulator and generate screenshots for GooglePlay")); + QCommandLineOption const googlePlayScreenshotOption( + QStringLiteral("sg"), + QCoreApplication::translate("main", + "Run simulator and generate screenshots for GooglePlay")); parser.addOption(googlePlayScreenshotOption); - QCommandLineOption iosScreenshotOption(QStringLiteral("si"), - QCoreApplication::translate("main", "Run simulator and generate screenshots for iOS")); + QCommandLineOption const iosScreenshotOption( + QStringLiteral("si"), + QCoreApplication::translate("main", "Run simulator and generate screenshots for iOS")); parser.addOption(iosScreenshotOption); - QCommandLineOption manualScreenshotOption(QStringLiteral("sm"), - QCoreApplication::translate("main", "Run simulator and generate screenshots for the manual")); + QCommandLineOption const manualScreenshotOption( + QStringLiteral("sm"), + QCoreApplication::translate("main", + "Run simulator and generate screenshots for the manual")); parser.addOption(manualScreenshotOption); - QCommandLineOption extractStringOption("string", - QCoreApplication::translate("main", "look up string using Librarian::getStringFromRessource and print it to stdout"), - QCoreApplication::translate("main", "string name")); + QCommandLineOption const extractStringOption( + "string", + QCoreApplication::translate( + "main", "look up string using Librarian::getStringFromRessource and print it to stdout"), + QCoreApplication::translate("main", "string name")); parser.addOption(extractStringOption); parser.addPositionalArgument(QStringLiteral("[fileName]"), QCoreApplication::translate("main", "File to import.")); parser.process(app); @@ -160,7 +167,7 @@ auto main(int argc, char *argv[]) -> int { parser.showHelp(-1); } - QString stringName = parser.value(extractStringOption); + QString const stringName = parser.value(extractStringOption); if (!stringName.isEmpty()) { QTextStream out(stdout); diff --git a/src/platform/FileExchange_Abstract.cpp b/src/platform/FileExchange_Abstract.cpp index 0be4ec49f..998d95491 100644 --- a/src/platform/FileExchange_Abstract.cpp +++ b/src/platform/FileExchange_Abstract.cpp @@ -203,6 +203,8 @@ void Platform::FileExchange_Abstract::processText(const QString& text) { return; } + +#if __has_include () if (text.contains(u"maps.app.goo.gl"_qs)) { emit resolveURL(text, QUrl(text).host()); @@ -213,6 +215,8 @@ void Platform::FileExchange_Abstract::processText(const QString& text) emit resolveURL(text, QUrl(text).host()); return; } +#endif + emit unableToProcessText(text); } diff --git a/src/platform/FileExchange_MacOS.cpp b/src/platform/FileExchange_MacOS.cpp index f1913f971..54aa69324 100644 --- a/src/platform/FileExchange_MacOS.cpp +++ b/src/platform/FileExchange_MacOS.cpp @@ -18,6 +18,12 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include +#include +#include +#include +#include + #include "platform/FileExchange_MacOS.h" @@ -25,20 +31,9 @@ Platform::FileExchange::FileExchange(QObject *parent) : FileExchange_Abstract(parent) { - // Standard constructor. Recall that the constructor must not call virtual functions. - // If you need virtual functions, use the methode deferredInitialization below. -#warning Not implemented } -/*void Platform::FileExchange::deferredInitialization() -{ - // This method is called immediately after the instance has been constructed. - // It can be used to implement initialization that calls virtual methods. -#warning Not implemented -}*/ - - // // Methods @@ -46,25 +41,54 @@ Platform::FileExchange::FileExchange(QObject *parent) void Platform::FileExchange::importContent() { - // Desktop: open file dialog and call processFileOpenRequest() on the filename. - // Mobile platforms: do nothing -#warning not implemented + auto fileNameX = QFileDialog::getOpenFileName(nullptr, tr("Import data"), QDir::homePath(), tr("All files (*)")); + if (!fileNameX.isEmpty()) + { + processFileOpenRequest(fileNameX); + } } -auto Platform::FileExchange::shareContent(const QByteArray & /*content*/, - const QString & /*mimeType*/, - const QString & /*fileNameTemplate*/) -> QString + +auto Platform::FileExchange::shareContent(const QByteArray& content, const QString& mimeType, const QString& fileNameTemplate) -> QString { - // Share file content -#warning not implemented + QMimeDatabase const mimeDataBase; + QMimeType const mime = mimeDataBase.mimeTypeForName(mimeType); + + auto fileNameX = QFileDialog::getSaveFileName(nullptr, tr("Export flight route"), QDir::homePath()+"/"+fileNameTemplate+"."+mime.preferredSuffix(), tr("%1 (*.%2);;All files (*)").arg(mime.comment(), mime.preferredSuffix())); + if (fileNameX.isEmpty()) + { + return QStringLiteral("abort"); + } + QFile file(fileNameX); + if (!file.open(QIODevice::WriteOnly)) + { + return tr("Unable to open file %1.").arg(fileNameX); + } + + if (file.write(content) != content.size()) + { + return tr("Unable to write to file %1.").arg(fileNameX); + } + file.close(); return {}; } -auto Platform::FileExchange::viewContent(const QByteArray & /*content*/, - const QString & /*mimeType*/, - const QString & /*fileNameTemplate*/) -> QString + +auto Platform::FileExchange::viewContent(const QByteArray& content, const QString& /*mimeType*/, const QString& fileNameTemplate) -> QString { - // View file content -#warning not implemented - return {}; + QTemporaryFile tmpFile(fileNameTemplate.arg(QStringLiteral("XXXXXX"))); + tmpFile.setAutoRemove(false); + if (!tmpFile.open()) { + return tr("Unable to open temporary file."); + } + tmpFile.write(content); + tmpFile.close(); + + bool const success = QDesktopServices::openUrl( + QUrl("file://" + tmpFile.fileName(), QUrl::TolerantMode)); + if (success) + { + return {}; + } + return tr("Unable to open data in other app."); } diff --git a/src/qml/pages/FlightRouteEditor.qml b/src/qml/pages/FlightRouteEditor.qml index 17a8a22d6..0e9b794d8 100644 --- a/src/qml/pages/FlightRouteEditor.qml +++ b/src/qml/pages/FlightRouteEditor.qml @@ -398,7 +398,7 @@ Page { MenuItem { text: qsTr("Reverse") - enabled: (Navigator.flightRoute.size > 0) && (sv.currentIndex === 0) + enabled: (Navigator.flightRoute.size > 1) && (sv.currentIndex === 0) onTriggered: { PlatformAdaptor.vibrateBrief()