diff --git a/CMakeLists.txt b/CMakeLists.txt index bf588f3..cf10ccf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,62 +1,62 @@ project(angelfish) cmake_minimum_required(VERSION 2.8.12) set(KF5_MIN_VERSION "5.18.0") set(QT_MIN_VERSION "5.5.0") option(BUILD_TESTING "Build test programs" ON) option(BUILD_WEBAPP_CONTAINER "Build webapp container" ON) ################# Disallow in-source build ################# if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message(FATAL_ERROR "This application requires an out of source build. Please create a separate build directory.") endif() include(FeatureSummary) ################# set KDE specific information ################# find_package(ECM 5.64.0 REQUIRED NO_MODULE) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMSetupVersion) include(ECMGenerateHeaders) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEClangFormat) include(ECMPoQmTools) include(KDECompilerSettings NO_POLICY_SCOPE) ################# Find dependencies ################# find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Quick Test Gui Svg QuickControls2 Sql) -find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 Purpose I18n) +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 Purpose I18n Config) # Necessary to support QtWebEngine installed in a different prefix than the rest of Qt (e.g flatpak) find_package(Qt5WebEngine REQUIRED) ################# Definitions to pass to the compiler ################# add_definitions(-DQT_NO_FOREACH) kde_enable_exceptions() ################# build and install ################# add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(autotests) endif() if (BUILD_WEBAPP_CONTAINER) add_subdirectory(angelfish-webapp) endif() install(PROGRAMS org.kde.mobile.angelfish.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES org.kde.mobile.angelfish.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES org.kde.mobile.angelfish.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) diff --git a/angelfish-webapp/CMakeLists.txt b/angelfish-webapp/CMakeLists.txt index 323dcd5..32de706 100644 --- a/angelfish-webapp/CMakeLists.txt +++ b/angelfish-webapp/CMakeLists.txt @@ -1,33 +1,33 @@ -find_package(KF5 REQUIRED COMPONENTS Config CoreAddons) +find_package(KF5 REQUIRED COMPONENTS CoreAddons) set(angelfish_webapp_SRCS main.cpp ../src/browsermanager.cpp ../src/bookmarkshistorymodel.cpp ../src/dbmanager.cpp ../src/iconimageprovider.cpp ../src/sqlquerymodel.cpp ../src/urlutils.cpp ../src/useragent.cpp ../src/tabsmodel.cpp webapp-resources.qrc ../src/resources.qrc ) add_executable(angelfish-webapp ${angelfish_webapp_SRCS} ${RESOURCES} ${WEBAPP_RESOURCES}) target_include_directories(angelfish-webapp PRIVATE ../src/) target_link_libraries(angelfish-webapp Qt5::Core Qt5::Qml Qt5::Quick Qt5::Sql Qt5::Svg Qt5::WebEngine KF5::I18n KF5::CoreAddons KF5::ConfigCore KF5::ConfigGui ) install(TARGETS angelfish-webapp ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/angelfish-webapp/main.cpp b/angelfish-webapp/main.cpp index 14e679b..42aa866 100644 --- a/angelfish-webapp/main.cpp +++ b/angelfish-webapp/main.cpp @@ -1,118 +1,118 @@ /* Copyright (C) 2019 Jonah Brüchert This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "bookmarkshistorymodel.h" #include "browsermanager.h" #include "iconimageprovider.h" #include "tabsmodel.h" #include "urlutils.h" #include "useragent.h" Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // Setup QtWebEngine qputenv("QTWEBENGINE_DIALOG_SET", "QtQuickControls2"); #if QT_VERSION > QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif QApplication app(argc, argv); //QCoreApplication::setOrganizationName("KDE"); //QCoreApplication::setOrganizationDomain("mobile.kde.org"); //QCoreApplication::setApplicationName("angelfish"); #if QT_VERSION <= QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif // Command line parser QCommandLineParser parser; parser.addPositionalArgument("desktopfile", i18n("desktop file to open"), "[file]"); parser.addHelpOption(); parser.process(app); // QML loading QQmlApplicationEngine engine; engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider(&engine)); if (parser.positionalArguments().isEmpty()) { return 1; } - + const QString fileName = parser.positionalArguments().first(); KDesktopFile desktopFile(fileName); if (desktopFile.readUrl().isEmpty()) { return 2; } const QString initialUrl = QUrl::fromUserInput(desktopFile.readUrl()).toString(); const QString appName = desktopFile.readName().toLower().replace(QLatin1Char(' '), QLatin1Char('-')) + QLatin1String("-angelfish-webapp"); KAboutData aboutData(appName.toLower(), desktopFile.readName(), QStringLiteral("0.1"), i18n("Angelfish Web App runtime"), KAboutLicense::GPL, i18n("Copyright 2020 Angelfish developers")); QApplication::setWindowIcon(QIcon::fromTheme(desktopFile.readIcon())); aboutData.addAuthor(i18n("Marco Martin"), QString(), "mart@kde.org"); - + KAboutData::setApplicationData(aboutData); // Exported types qmlRegisterType("org.kde.mobile.angelfish", 1, 0, "BookmarksHistoryModel"); qmlRegisterType("org.kde.mobile.angelfish", 1, 0, "UserAgentGenerator"); qmlRegisterType("org.kde.mobile.angelfish", 1, 0, "TabsModel"); // URL utils qmlRegisterSingletonType("org.kde.mobile.angelfish", 1, 0, "UrlUtils", [](QQmlEngine *, QJSEngine *) -> QObject * { return static_cast(new UrlUtils()); }); BrowserManager::instance()->setInitialUrl(initialUrl); // Browser Manager qmlRegisterSingletonType("org.kde.mobile.angelfish", 1, 0, "BrowserManager", [](QQmlEngine *, QJSEngine *) -> QObject * { return static_cast(BrowserManager::instance()); }); // Load QML engine.load(QUrl(QStringLiteral("qrc:///webapp.qml"))); // Error handling if (engine.rootObjects().isEmpty()) { return -1; } return app.exec(); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8e489e..c0ffd5d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,18 +1,28 @@ set(angelfish_SRCS main.cpp browsermanager.cpp bookmarkshistorymodel.cpp dbmanager.cpp iconimageprovider.cpp sqlquerymodel.cpp urlutils.cpp useragent.cpp tabsmodel.cpp + desktopfilegenerator.cpp ) qt5_add_resources(RESOURCES resources.qrc) add_executable(angelfish ${angelfish_SRCS} ${RESOURCES}) -target_link_libraries(angelfish Qt5::Core Qt5::Qml Qt5::Quick Qt5::Sql Qt5::Svg Qt5::WebEngine KF5::I18n) +target_link_libraries(angelfish + Qt5::Core + Qt5::Qml + Qt5::Quick + Qt5::Sql + Qt5::Svg + Qt5::WebEngine + KF5::I18n + KF5::ConfigCore +) install(TARGETS angelfish ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/contents/ui/webbrowser.qml b/src/contents/ui/webbrowser.qml index 6dc2a0d..c8c43d2 100644 --- a/src/contents/ui/webbrowser.qml +++ b/src/contents/ui/webbrowser.qml @@ -1,406 +1,415 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * * * ***************************************************************************/ import QtQuick 2.1 import QtWebEngine 1.6 import QtQuick.Window 2.3 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 as QtSettings import org.kde.kirigami 2.7 as Kirigami import org.kde.mobile.angelfish 1.0 import QtQuick.Layouts 1.2 Kirigami.ApplicationWindow { id: webBrowser title: i18n("Angelfish Web Browser") /** Pointer to the currently active view. * * Browser-level functionality should use this to refer to the current * view, rather than looking up views in the mode, as far as possible. */ property Item currentWebView: tabs.currentItem // Pointer to the currently active list of tabs. // // As there are private and normal tabs, switch between // them according to the current mode. property ListWebView tabs: rootPage.privateMode ? privateTabs : regularTabs // Pointer to browser settings property Settings settings: settings // Used to determine if the window is in landscape mode property bool landscape: width > height onCurrentWebViewChanged: { print("Current WebView is now : " + tabs.currentIndex); } property int borderWidth: Math.round(Kirigami.Units.gridUnit / 18); property color borderColor: Kirigami.Theme.highlightColor; width: Kirigami.Units.gridUnit * 20 height: Kirigami.Units.gridUnit * 30 pageStack.globalToolBar.showNavigationButtons: { if (pageStack.depth <= 1) return Kirigami.ApplicationHeaderStyle.None; if (pageStack.currentIndex === pageStack.depth - 1) return Kirigami.ApplicationHeaderStyle.ShowBackButton; // not used so far, but maybe in future return (Kirigami.ApplicationHeaderStyle.ShowBackButton | Kirigami.ApplicationHeaderStyle.ShowForwardButton); } globalDrawer: Kirigami.GlobalDrawer { id: globalDrawer handleVisible: false actions: [ Kirigami.Action { icon.name: "tab-duplicate" onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("Tabs.qml")) } text: i18n("Tabs") }, Kirigami.Action { icon.name: "view-private" onTriggered: { rootPage.privateMode ? rootPage.privateMode = false : rootPage.privateMode = true } text: rootPage.privateMode ? i18n("Leave private mode") : i18n("Private mode") }, Kirigami.Action { icon.name: "bookmarks" onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("Bookmarks.qml")) } text: i18n("Bookmarks") }, Kirigami.Action { icon.name: "view-history" onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("History.qml")) } text: i18n("History") }, Kirigami.Action { icon.name: "configure" text: i18n("Settings") onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) } } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer handleVisible: false } // Main Page pageStack.initialPage: Kirigami.Page { id: rootPage leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None Kirigami.ColumnView.fillWidth: true Kirigami.ColumnView.pinned: true Kirigami.ColumnView.preventStealing: true // Required to enforce active tab reload // on start. As a result, mixed isMobile // tabs will work correctly property bool initialized: false property bool privateMode: false // Used for automatically show or hid navigation // bar. Set separately to combine with other options // for navigation bar management (webapp and others) property bool navigationAutoShow: true ListWebView { id: regularTabs anchors.fill: parent activeTabs: rootPage.initialized && !rootPage.privateMode } ListWebView { id: privateTabs anchors.fill: parent activeTabs: rootPage.initialized && rootPage.privateMode privateTabsMode: true } ErrorHandler { id: errorHandler errorString: currentWebView.errorString errorCode: currentWebView.errorCode anchors { top: parent.top left: parent.left right: parent.right bottom: navigation.top } visible: currentWebView.errorCode !== "" } Loader { id: questionLoader anchors.bottom: navigation.top anchors.left: parent.left anchors.right: parent.right } // Container for the progress bar Item { id: progressItem height: Math.round(Kirigami.Units.gridUnit / 6) z: navigation.z + 1 anchors { bottom: navigation.top bottomMargin: -Math.round(height / 2) left: tabs.left right: tabs.right } opacity: currentWebView.loading ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } Rectangle { color: Kirigami.Theme.highlightColor width: Math.round((currentWebView.loadProgress / 100) * parent.width) anchors { top: parent.top left: parent.left bottom: parent.bottom } } } Loader { id: sheetLoader } // The menu at the bottom right contextualActions: [ Kirigami.Action { icon.name: "edit-find" shortcut: "Ctrl+F" onTriggered: { if (!sheetLoader.item || !sheetLoader.item.sheetOpen) { sheetLoader.setSource("FindInPageSheet.qml") sheetLoader.item.open() } } text: i18n("Find in page") }, Kirigami.Action { icon.name: "document-share" text: i18n("Share page") onTriggered: { sheetLoader.setSource("ShareSheet.qml") sheetLoader.item.url = currentWebView.url sheetLoader.item.title = currentWebView.title sheetLoader.item.open() } }, + Kirigami.Action { + icon.name: "list-add" + text: i18n("Add to homescreen") + onTriggered: { + DesktopFileGenerator.createDesktopFile(currentWebView.title, + currentWebView.url, + currentWebView.icon) + } + }, Kirigami.Action { enabled: currentWebView.canGoBack icon.name: "go-previous" text: i18n("Go previous") onTriggered: { currentWebView.goBack() } }, Kirigami.Action { enabled: currentWebView.canGoForward icon.name: "go-next" text: i18n("Go forward") onTriggered: { currentWebView.goForward() } }, Kirigami.Action { icon.name: currentWebView.loading ? "process-stop" : "view-refresh" text: currentWebView.loading ? i18n("Stop loading") : i18n("Refresh") onTriggered: { currentWebView.loading ? currentWebView.stopLoading() : currentWebView.reload() } }, Kirigami.Action { icon.name: "bookmarks" text: i18n("Add bookmark") onTriggered: { print("Adding bookmark"); var request = new Object;// FIXME request.url = currentWebView.url; request.title = currentWebView.title; request.icon = currentWebView.icon; BrowserManager.addBookmark(request); } }, Kirigami.Action { icon.name: "computer" text: i18n("Show desktop site") checkable: true checked: !currentWebView.userAgent.isMobile onTriggered: { currentWebView.userAgent.isMobile = !currentWebView.userAgent.isMobile; } }, Kirigami.Action { icon.name: "edit-select-text" text: rootPage.navigationAutoShow ? i18n("Hide navigation bar") : i18n("Show navigation bar") visible: navigation.visible onTriggered: { if (!navigation.visible) return; rootPage.navigationAutoShow = !rootPage.navigationAutoShow; } } ] // Bottom navigation bar Navigation { id: navigation navigationShown: visible && rootPage.navigationAutoShow && webBrowser.visibility !== Window.FullScreen visible: webBrowser.visibility !== Window.FullScreen Kirigami.Theme.colorSet: rootPage.privateMode ? Kirigami.Theme.Complementary : Kirigami.Theme.Window layer.enabled: navigation.navigationShown layer.effect: DropShadow { verticalOffset: - 1 color: Kirigami.Theme.disabledTextColor samples: 10 spread: 0.1 cached: true // element is static } anchors { bottom: parent.bottom left: parent.left right: parent.right } onActivateUrlEntry: urlEntry.open() } NavigationEntrySheet { id: urlEntry } // Thin line above navigation Rectangle { height: webBrowser.borderWidth color: webBrowser.borderColor anchors { left: parent.left bottom: navigation.top right: parent.right } visible: navigation.navigationShown } // dealing with hiding and showing navigation bar property point oldScrollPosition: Qt.point(0, 0) property bool pageAlmostReady: !currentWebView.loading || currentWebView.loadProgress > 90 onPageAlmostReadyChanged: { if (!rootPage.pageAlmostReady) rootPage.navigationAutoShow = true; else rootPage.oldScrollPosition = currentWebView.scrollPosition; } Connections { target: currentWebView onScrollPositionChanged: { var delta = 100; if (rootPage.navigationAutoShow && rootPage.pageAlmostReady) { if (rootPage.oldScrollPosition.y + delta < currentWebView.scrollPosition.y) { // hide navbar rootPage.oldScrollPosition = currentWebView.scrollPosition; rootPage.navigationAutoShow = false; } else if (rootPage.oldScrollPosition.y > currentWebView.scrollPosition.y) { // navbar open and scrolling up rootPage.oldScrollPosition = currentWebView.scrollPosition; } } else if (!rootPage.navigationAutoShow) { if (rootPage.oldScrollPosition.y - delta > currentWebView.scrollPosition.y) { // show navbar rootPage.oldScrollPosition = currentWebView.scrollPosition; rootPage.navigationAutoShow = true; } else if (rootPage.oldScrollPosition.y < currentWebView.scrollPosition.y) { // navbar closed and scrolling down rootPage.oldScrollPosition = currentWebView.scrollPosition; } } } } } Connections { target: webBrowser.pageStack onCurrentIndexChanged: { // drop all sub pages as soon as the browser window is the // focussed one if (webBrowser.pageStack.currentIndex === 0) popSubPages(); } } QtSettings.Settings { // kept separate to simplify definition of aliases property alias x: webBrowser.x property alias y: webBrowser.y property alias width: webBrowser.width property alias height: webBrowser.height } Settings { id: settings } Component.onCompleted: rootPage.initialized = true function popSubPages() { while (webBrowser.pageStack.depth > 1) webBrowser.pageStack.pop(); } } diff --git a/src/desktopfilegenerator.cpp b/src/desktopfilegenerator.cpp new file mode 100644 index 0000000..590ce65 --- /dev/null +++ b/src/desktopfilegenerator.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * * + * Copyright 2020 Jonah Brüchert * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + * * + ***************************************************************************/ + + +#include "desktopfilegenerator.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +DesktopFileGenerator::DesktopFileGenerator(QQmlEngine *engine, QObject *parent) + : QObject(parent) + , m_engine(engine) +{ +} + +void DesktopFileGenerator::createDesktopFile(const QString &name, const QString &url, const QString &icon) +{ + QString location = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); + QString filename = name.toLower(); + filename.replace(" ", "_"); + QString path = QStringLiteral("%1/%2.desktop").arg(location, filename); + KConfig desktopFile(path, KConfig::SimpleConfig); + + storeIcon(icon, filename); + + auto desktopEntry = desktopFile.group("Desktop Entry"); + desktopEntry.writeEntry(QStringLiteral("URL"), url); + desktopEntry.writeEntry(QStringLiteral("Name"), name); + desktopEntry.writeEntry(QStringLiteral("Exec"), QStringLiteral("%1 %2.desktop").arg(webappCommand(), filename)); + desktopEntry.writeEntry(QStringLiteral("Icon"), filename); + + desktopFile.sync(); +} + +void DesktopFileGenerator::storeIcon(const QString &url, const QString &fileName) +{ + auto *provider = static_cast(m_engine->imageProvider("favicon")); + + QLatin1String prefix_favicon = QLatin1String("image://favicon/"); + QString providerIconName = url.mid(prefix_favicon.size()); + + QSize szRequested; + QSize szObtained; + + QString iconLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + "/icons/hicolor/16x16/apps/"; + + QDir().mkpath(iconLocation); + + auto imageFile = new QFile(iconLocation + fileName + ".png"); + + if (!imageFile->open(QIODevice::WriteOnly)) { + qDebug() << Q_FUNC_INFO << "Failed to open image file"; + } + + switch (provider->imageType()) { + case QQmlImageProviderBase::Image: { + QImage image = provider->requestImage(providerIconName, &szObtained, szRequested); + if (!image.save(imageFile, "PNG")) { + qWarning() << Q_FUNC_INFO << "Failed to save image" << url; + return; + } + break; + } + case QQmlImageProviderBase::Pixmap: { + QPixmap image = provider->requestPixmap(providerIconName, &szObtained, szRequested); + if (!image.save(imageFile, "PNG")) { + qWarning() << Q_FUNC_INFO << "Failed to save pixmap" << url; + return; + } + break; + } + default: + qDebug() << "Failed to save unhandled image type"; + } +} + +QString DesktopFileGenerator::webappCommand() +{ + if (!QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty()) { + return QStringLiteral("flatpak run " + "--command=angelfish-webapp " + "--filesystem=%1 " + "org.kde.mobile.angelfish") + .arg(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)); + } + + return QStringLiteral("angelfish-webapp"); +} diff --git a/src/desktopfilegenerator.h b/src/desktopfilegenerator.h new file mode 100644 index 0000000..465545b --- /dev/null +++ b/src/desktopfilegenerator.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * * + * Copyright 2020 Jonah Brüchert * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + * * + ***************************************************************************/ + +#ifndef DESKTOPFILEGENERATOR_H +#define DESKTOPFILEGENERATOR_H + +#include +class QQmlEngine; + +class DesktopFileGenerator : public QObject +{ + Q_OBJECT +public: + explicit DesktopFileGenerator(QQmlEngine *engine, QObject *parent = nullptr); + + Q_INVOKABLE void createDesktopFile(const QString &name, const QString &url, const QString &icon); + +private: + void storeIcon(const QString &url, const QString &fileName); + QString webappCommand(); + QQmlEngine *m_engine; +}; + +#endif // DESKTOPFILEGENERATOR_H diff --git a/src/main.cpp b/src/main.cpp index df7d495..d07814a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,99 +1,104 @@ /* Copyright (C) 2019 Jonah Brüchert This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "bookmarkshistorymodel.h" #include "browsermanager.h" #include "iconimageprovider.h" #include "tabsmodel.h" #include "urlutils.h" #include "useragent.h" +#include "desktopfilegenerator.h" Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // Setup QtWebEngine qputenv("QTWEBENGINE_DIALOG_SET", "QtQuickControls2"); #if QT_VERSION > QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif QApplication app(argc, argv); QCoreApplication::setOrganizationName("KDE"); QCoreApplication::setOrganizationDomain("mobile.kde.org"); QCoreApplication::setApplicationName("angelfish"); #if QT_VERSION <= QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif // Command line parser QCommandLineParser parser; parser.addPositionalArgument("url", i18n("URL to open"), "[url]"); parser.addHelpOption(); parser.process(app); // QML loading QQmlApplicationEngine engine; engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider(&engine)); // initial url command line parameter QString initialUrl; if (!parser.positionalArguments().isEmpty()) initialUrl = QUrl::fromUserInput(parser.positionalArguments().first()).toString(); // Exported types qmlRegisterType("org.kde.mobile.angelfish", 1, 0, "BookmarksHistoryModel"); qmlRegisterType("org.kde.mobile.angelfish", 1, 0, "UserAgentGenerator"); qmlRegisterType("org.kde.mobile.angelfish", 1, 0, "TabsModel"); // URL utils qmlRegisterSingletonType("org.kde.mobile.angelfish", 1, 0, "UrlUtils", [](QQmlEngine *, QJSEngine *) -> QObject * { return static_cast(new UrlUtils()); }); BrowserManager::instance()->setInitialUrl(initialUrl); // Browser Manager qmlRegisterSingletonType("org.kde.mobile.angelfish", 1, 0, "BrowserManager", [](QQmlEngine *, QJSEngine *) -> QObject * { return static_cast(BrowserManager::instance()); }); + qmlRegisterSingletonType("org.kde.mobile.angelfish", 1, 0, "DesktopFileGenerator", [](QQmlEngine *engine, QJSEngine *) -> QObject * { + return static_cast(new DesktopFileGenerator(engine)); + }); + // Load QML engine.load(QUrl(QStringLiteral("qrc:///webbrowser.qml"))); // Error handling if (engine.rootObjects().isEmpty()) { return -1; } return app.exec(); }