diff options
30 files changed, 435 insertions, 349 deletions
diff --git a/doc/Development.asciidoc b/doc/Development.asciidoc index 2968b7c..c6c49fb 100644 --- a/doc/Development.asciidoc +++ b/doc/Development.asciidoc @@ -17,6 +17,9 @@ Smolbote is licensed under GPLv3 _only_, and explicitly does not use any self-updating "-or-later" licensing. Your code needs to be compatible with it, and have a license header in the style used by the project. +Interfaces (located in include/) should be licensed under MIT. This is to allow +more license compatibility for plugins. + === SPDX-License-Identifier A full list of license identifiers can be found on https://spdx.org/licenses/[the SPDX website]. diff --git a/include/browserinterface.h b/include/browserinterface.h new file mode 100644 index 0000000..25de65c --- /dev/null +++ b/include/browserinterface.h @@ -0,0 +1,24 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +class Profile; +class BrowserInterface +{ +public: + // configuration access + virtual const QStringList configurationOptions() const = 0; + virtual const QString configuration(const QString &key) const = 0; + virtual void setConfiguration(const QString &key, const QString &value) = 0; + + // profile access + virtual const QList<QPair<QString, Profile *>> profileList() const = 0; + virtual QPair<QString, Profile *> loadProfile(const QString &id, bool isOffTheRecord = true) = 0; + virtual void removeProfile(const QString &id) = 0; +}; diff --git a/include/plugininterface.h b/include/plugininterface.h new file mode 100644 index 0000000..103ee62 --- /dev/null +++ b/include/plugininterface.h @@ -0,0 +1,35 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include <QtPlugin> +#include <QHash> +#include <memory> +#include <functional> +#include "browserinterface.h" +#include <QApplication> + +typedef QHash<QString, std::function<int()>> CommandHash_t; + +class QDialog; +class PluginInterface +{ +public: + virtual ~PluginInterface() = default; + virtual CommandHash_t commands() = 0; + virtual QDialog *createWidget(QWidget *parent = nullptr) const = 0; + +protected: + BrowserInterface *browser() const { + return dynamic_cast<BrowserInterface *>(qApp); + } +}; + +#define PluginInterfaceIid "net.iserlohn-fortress.smolbote.PluginInterface" +Q_DECLARE_INTERFACE(PluginInterface, PluginInterfaceIid) diff --git a/include/profileinterface.h b/include/profileinterface.h new file mode 100644 index 0000000..c24a4ae --- /dev/null +++ b/include/profileinterface.h @@ -0,0 +1,84 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include <QString> +#include <QUrl> +#include <QWebEngineProfile> +#include <QWebEngineSettings> +#include <QNetworkCookie> + +class Profile : public QWebEngineProfile +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString search READ search WRITE setSearch NOTIFY searchChanged) + Q_PROPERTY(QUrl homepage READ homepage WRITE setHomepage NOTIFY homepageChanged) + Q_PROPERTY(QUrl newtab READ newtab WRITE setNewtab NOTIFY newtabChanged) + + // QWebEngineProfile should-be properties + Q_PROPERTY(QString cachePath READ cachePath WRITE setCachePath NOTIFY propertyChanged) + Q_PROPERTY(QString persistentStoragePath READ persistentStoragePath WRITE setPersistentStoragePath NOTIFY propertyChanged) + Q_PROPERTY(int persistentCookiesPolicy READ persistentCookiesPolicy WRITE setPersistentCookiesPolicy NOTIFY propertyChanged) + + Q_PROPERTY(QString httpAcceptLanguage READ httpAcceptLanguage WRITE setHttpAcceptLanguage NOTIFY propertyChanged) + Q_PROPERTY(int httpCacheMaximumSize READ httpCacheMaximumSize WRITE setHttpCacheMaximumSize NOTIFY propertyChanged) + Q_PROPERTY(int httpCacheType READ httpCacheType WRITE setHttpCacheType NOTIFY propertyChanged) + Q_PROPERTY(QString httpUserAgent READ httpUserAgent WRITE setHttpUserAgent NOTIFY propertyChanged) + + Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY propertyChanged) + +protected: + explicit Profile(QObject *parent = nullptr) : QWebEngineProfile(parent) {}; + explicit Profile(const QString &storageName, QObject *parent = nullptr) : QWebEngineProfile(storageName, parent) {}; + +public: + virtual const QString name() const = 0; + virtual void setName(const QString &name) = 0; + + virtual const QVector<QNetworkCookie> cookies() const = 0; + virtual const QMap<QByteArray, QByteArray> headers() const = 0; + + // search url + virtual QString search() const = 0; + virtual void setSearch(const QString &url) = 0; + + // homepage url + virtual QUrl homepage() const = 0; + virtual void setHomepage(const QUrl &url) = 0; + + // new tab url + virtual QUrl newtab() const = 0; + virtual void setNewtab(const QUrl &url) = 0; + + virtual void setCachePath(const QString &path) = 0; + virtual void setPersistentStoragePath(const QString &path) = 0; + virtual void setPersistentCookiesPolicy(int policy) = 0; + + virtual void setHttpAcceptLanguage(const QString &httpAcceptLanguage) = 0; + virtual void setHttpCacheMaximumSize(int maxSize) = 0; + virtual void setHttpCacheType(int type) = 0; + virtual void setHttpUserAgent(const QString &userAgent) = 0; + virtual void setHttpHeader(const QString &name, const QString &value) = 0; + virtual void removeHttpHeader(const QString &name) = 0; + + virtual void setSpellCheckEnabled(bool enable) = 0; + +signals: + void nameChanged(const QString &name); + void searchChanged(const QString &url); + void homepageChanged(const QUrl &url); + void newtabChanged(const QUrl &url); + + void propertyChanged(const QString &name, const QVariant &value); + void attributeChanged(const QWebEngineSettings::WebAttribute attribute, const bool value); + void headerChanged(const QString &name, const QString &value); + void headerRemoved(const QString &name); +}; diff --git a/lib/web/CMakeLists.txt b/lib/web/CMakeLists.txt index 86f7f1c..12b4080 100644 --- a/lib/web/CMakeLists.txt +++ b/lib/web/CMakeLists.txt @@ -11,4 +11,8 @@ add_library(web webprofile.h ) +target_include_directories(web + PRIVATE ${PROJECT_SOURCE_DIR}/include +) + target_link_libraries(web Qt5::WebEngineWidgets) diff --git a/lib/web/profilemanager.cpp b/lib/web/profilemanager.cpp index 17435e8..14795cd 100644 --- a/lib/web/profilemanager.cpp +++ b/lib/web/profilemanager.cpp @@ -17,6 +17,19 @@ ProfileManager::ProfileManager(const QHash<QString, QString> &profileSection, QO { } +WebProfile *ProfileManager::createProfile(const QString& id, bool isOffTheRecord) +{ + QDir profileDir(defaults.value("profile.path")); + const QString path = profileDir.absoluteFilePath(id + ".profile"); + { + QSettings init(path, QSettings::IniFormat); + init.setValue("name", id); + init.setValue("otr", isOffTheRecord); + init.sync(); + } + return loadProfile(path); +} + WebProfile *ProfileManager::loadProfile(const QString &path) { std::unique_ptr<ProfileData> ptr = std::make_unique<ProfileData>(path); diff --git a/lib/web/profilemanager.h b/lib/web/profilemanager.h index c7ed660..f5e33b9 100644 --- a/lib/web/profilemanager.h +++ b/lib/web/profilemanager.h @@ -27,6 +27,7 @@ class ProfileManager : public QObject public: explicit ProfileManager(const QHash<QString, QString> &profileSection, QObject *parent); + WebProfile *createProfile(const QString &id, bool isOffTheRecord); WebProfile *loadProfile(const QString &path); void deleteProfile(const QString &id); diff --git a/lib/web/webprofile.cpp b/lib/web/webprofile.cpp index 16c12b7..38aa7f0 100644 --- a/lib/web/webprofile.cpp +++ b/lib/web/webprofile.cpp @@ -15,7 +15,7 @@ WebProfile *WebProfile::profile = nullptr; WebProfile::WebProfile(const QString &name, QObject *parent) - : QWebEngineProfile(parent) + : Profile(parent) { m_name = name; @@ -32,7 +32,7 @@ WebProfile::WebProfile(const QString &name, QObject *parent) } WebProfile::WebProfile(const QString &storageName, const QString &name, QObject *parent) - : QWebEngineProfile(storageName, parent) + : Profile(storageName, parent) { m_name = name; diff --git a/lib/web/webprofile.h b/lib/web/webprofile.h index 269989b..a3b5b12 100644 --- a/lib/web/webprofile.h +++ b/lib/web/webprofile.h @@ -17,28 +17,12 @@ #include <QNetworkCookie> #include <QMap> #include <QWebEngineSettings> +#include <profileinterface.h> -class WebProfile : public QWebEngineProfile +class WebProfile : public Profile { Q_OBJECT - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(QString search READ search WRITE setSearch NOTIFY searchChanged) - Q_PROPERTY(QUrl homepage READ homepage WRITE setHomepage NOTIFY homepageChanged) - Q_PROPERTY(QUrl newtab READ newtab WRITE setNewtab NOTIFY newtabChanged) - - // QWebEngineProfile should-be properties - Q_PROPERTY(QString cachePath READ cachePath WRITE setCachePath NOTIFY propertyChanged) - Q_PROPERTY(QString persistentStoragePath READ persistentStoragePath WRITE setPersistentStoragePath NOTIFY propertyChanged) - Q_PROPERTY(int persistentCookiesPolicy READ persistentCookiesPolicy WRITE setPersistentCookiesPolicy NOTIFY propertyChanged) - - Q_PROPERTY(QString httpAcceptLanguage READ httpAcceptLanguage WRITE setHttpAcceptLanguage NOTIFY propertyChanged) - Q_PROPERTY(int httpCacheMaximumSize READ httpCacheMaximumSize WRITE setHttpCacheMaximumSize NOTIFY propertyChanged) - Q_PROPERTY(int httpCacheType READ httpCacheType WRITE setHttpCacheType NOTIFY propertyChanged) - Q_PROPERTY(QString httpUserAgent READ httpUserAgent WRITE setHttpUserAgent NOTIFY propertyChanged) - - Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY propertyChanged) - public: // off-the-record constructor explicit WebProfile(const QString &name, QObject *parent = nullptr); @@ -95,17 +79,6 @@ public: void setSpellCheckEnabled(bool enable); -signals: - void nameChanged(const QString &name); - void searchChanged(const QString &url); - void homepageChanged(const QUrl &url); - void newtabChanged(const QUrl &url); - - void propertyChanged(const QString &name, const QVariant &value); - void attributeChanged(const QWebEngineSettings::WebAttribute attribute, const bool value); - void headerChanged(const QString &name, const QString &value); - void headerRemoved(const QString &name); - private: static WebProfile *profile; diff --git a/plugins/ConfigurationEditor/CMakeLists.txt b/plugins/ConfigurationEditor/CMakeLists.txt index 22ef3d3..699366e 100644 --- a/plugins/ConfigurationEditor/CMakeLists.txt +++ b/plugins/ConfigurationEditor/CMakeLists.txt @@ -5,6 +5,9 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) add_library(ConfigurationEditorPlugin SHARED + ${CMAKE_SOURCE_DIR}/include/plugininterface.h + ${CMAKE_SOURCE_DIR}/include/browserinterface.h + configurationeditorplugin.cpp configurationeditorplugin.h forms/configurationdialog.cpp @@ -13,14 +16,11 @@ add_library(ConfigurationEditorPlugin SHARED ) target_include_directories(ConfigurationEditorPlugin - PRIVATE ${CMAKE_SOURCE_DIR}/plugins - PRIVATE ${CMAKE_SOURCE_DIR}/lib/configuration - PRIVATE ${Boost_INCLUDE_DIRS} + PRIVATE ${CMAKE_SOURCE_DIR}/include ) target_link_libraries(ConfigurationEditorPlugin PRIVATE Qt5::Widgets - PRIVATE configuration ) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") diff --git a/plugins/ConfigurationEditor/configurationeditorplugin.cpp b/plugins/ConfigurationEditor/configurationeditorplugin.cpp index bf8a90d..1f60cc0 100644 --- a/plugins/ConfigurationEditor/configurationeditorplugin.cpp +++ b/plugins/ConfigurationEditor/configurationeditorplugin.cpp @@ -1,8 +1,15 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + #include "configurationeditorplugin.h" #include "forms/configurationdialog.h" #include <QDialog> #include <QHash> -#include <configuration.h> CommandHash_t ConfigurationEditorPlugin::commands() { @@ -16,9 +23,23 @@ CommandHash_t ConfigurationEditorPlugin::commands() return hash; } -QDialog *ConfigurationEditorPlugin::createWidget(QWidget *parent) +QDialog *ConfigurationEditorPlugin::createWidget(QWidget *parent) const { - auto *dlg = new ConfigurationDialog(browser->getConfiguration(), parent); + auto *app = browser(); + Q_CHECK_PTR(app); + + auto *dlg = new ConfigurationDialog(parent); dlg->setAttribute(Qt::WA_DeleteOnClose, true); + + for(const QString &key : app->configurationOptions()) { + dlg->addOption(key, app->configuration(key)); + } + dlg->ui->settings_treeWidget->resizeColumnToContents(0); + dlg->ui->shortcuts_treeWidget->resizeColumnToContents(0); + + connect(dlg, &ConfigurationDialog::configurationChanged, dlg, [app](const QString &key, const QString &value) { + app->setConfiguration(key, value); + }); + return dlg; } diff --git a/plugins/ConfigurationEditor/configurationeditorplugin.h b/plugins/ConfigurationEditor/configurationeditorplugin.h index 5e70ab7..7e7ebc6 100644 --- a/plugins/ConfigurationEditor/configurationeditorplugin.h +++ b/plugins/ConfigurationEditor/configurationeditorplugin.h @@ -1,8 +1,16 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + #ifndef CONFIGURATIONEDITOR_PLUGIN_H #define CONFIGURATIONEDITOR_PLUGIN_H #include <QVector> -#include <interfaces.h> +#include <plugininterface.h> class QDialog; class ConfigurationEditorPlugin : public QObject, public PluginInterface @@ -14,7 +22,7 @@ class ConfigurationEditorPlugin : public QObject, public PluginInterface public: // PluginInterface CommandHash_t commands() override; - QDialog *createWidget(QWidget *parent = nullptr) override; + QDialog *createWidget(QWidget *parent = nullptr) const override; }; #endif // CONFIGURATIONEDITOR_PLUGIN_H diff --git a/plugins/ConfigurationEditor/forms/configurationdialog.cpp b/plugins/ConfigurationEditor/forms/configurationdialog.cpp index ffe7f9c..f433d70 100644 --- a/plugins/ConfigurationEditor/forms/configurationdialog.cpp +++ b/plugins/ConfigurationEditor/forms/configurationdialog.cpp @@ -1,41 +1,50 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + #include "configurationdialog.h" -#include "ui_configurationdialog.h" -#include <configuration.h> #include <QKeySequenceEdit> +#include <QLineEdit> -ConfigurationDialog::ConfigurationDialog(Configuration *conf, QWidget *parent) : +ConfigurationDialog::ConfigurationDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ConfigurationDialog) { ui->setupUi(this); - Q_CHECK_PTR(conf); - - for(const auto &option : conf->options()) { - auto path = QString::fromStdString(option->long_name()); - auto value = QString::fromStdString(conf->value<std::string>(option->long_name().c_str()).value_or("")); - - QTreeWidgetItem *item = nullptr; - if(path.contains("shortcut")) { - item = new QTreeWidgetItem(ui->shortcuts_treeWidget); - auto *edit = new QKeySequenceEdit(value); - connect(edit, &QKeySequenceEdit::keySequenceChanged, this, [=](const QKeySequence &keySequqnce) { - conf->setValue(qUtf8Printable(path), keySequqnce.toString()); - }); - ui->shortcuts_treeWidget->setItemWidget(item, 1, edit); - } else { - item = new QTreeWidgetItem(ui->settings_treeWidget); - item->setText(1, value); - } - - item->setText(0, path); - - } - - ui->settings_treeWidget->resizeColumnToContents(0); - ui->shortcuts_treeWidget->resizeColumnToContents(0); } ConfigurationDialog::~ConfigurationDialog() { delete ui; } + +void ConfigurationDialog::addOption(const QString &key, const QString &value) +{ + QTreeWidgetItem *item = nullptr; + + if(key.contains(QLatin1Literal("shortcut"))) { + item = new QTreeWidgetItem(ui->shortcuts_treeWidget); + + auto *edit = new QKeySequenceEdit(value); + ui->shortcuts_treeWidget->setItemWidget(item, 1, edit); + + connect(edit, &QKeySequenceEdit::keySequenceChanged, this, [this, key](const QKeySequence &keySequence) { + emit configurationChanged(key, keySequence.toString()); + }); + + } else { + item = new QTreeWidgetItem(ui->settings_treeWidget); + + auto *edit = new QLineEdit(value); + ui->settings_treeWidget->setItemWidget(item, 1, edit); + + connect(edit, &QLineEdit::editingFinished, this, [this, key, edit]() { + emit configurationChanged(key, edit->text()); + }); + } + item->setText(0, key); +} diff --git a/plugins/ConfigurationEditor/forms/configurationdialog.h b/plugins/ConfigurationEditor/forms/configurationdialog.h index 9d86f2d..fd07aa8 100644 --- a/plugins/ConfigurationEditor/forms/configurationdialog.h +++ b/plugins/ConfigurationEditor/forms/configurationdialog.h @@ -1,21 +1,32 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + #ifndef CONFIGURATIONDIALOG_H #define CONFIGURATIONDIALOG_H #include <QDialog> +#include "ui_configurationdialog.h" -namespace Ui { -class ConfigurationDialog; -} - -class Configuration; class ConfigurationDialog : public QDialog { Q_OBJECT + friend class ConfigurationEditorPlugin; + public: - explicit ConfigurationDialog(Configuration *conf, QWidget *parent = nullptr); + explicit ConfigurationDialog(QWidget *parent = nullptr); ~ConfigurationDialog(); + void addOption(const QString &key, const QString &value); + +signals: + void configurationChanged(const QString &key, const QString &value); + private: Ui::ConfigurationDialog *ui; }; diff --git a/plugins/ProfileEditor/CMakeLists.txt b/plugins/ProfileEditor/CMakeLists.txt index 7c9a774..5bcfcce 100644 --- a/plugins/ProfileEditor/CMakeLists.txt +++ b/plugins/ProfileEditor/CMakeLists.txt @@ -9,31 +9,35 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) add_library(ProfileEditorPlugin SHARED - profileeditorplugin.cpp - profileeditorplugin.h - forms/profileview.cpp - forms/profileview.h - forms/profileview.ui - forms/profilemanagerdialog.cpp - forms/profilemanagerdialog.h - forms/profilemanagerdialog.ui - forms/newprofiledialog.cpp - forms/newprofiledialog.h - forms/newprofiledialog.ui - forms/newhttpheaderdialog.cpp - forms/newhttpheaderdialog.h - forms/newhttpheaderdialog.ui + ${CMAKE_SOURCE_DIR}/include/profileinterface.h + ${CMAKE_SOURCE_DIR}/include/browserinterface.h + ${CMAKE_SOURCE_DIR}/include/plugininterface.h + + profileeditorplugin.cpp + profileeditorplugin.h + + forms/profilemanagerdialog.cpp + forms/profilemanagerdialog.h + forms/profilemanagerdialog.ui + forms/profileview.cpp + forms/profileview.h + forms/profileview.ui + + forms/newprofiledialog.cpp + forms/newprofiledialog.h + forms/newprofiledialog.ui + forms/newhttpheaderdialog.cpp + forms/newhttpheaderdialog.h + forms/newhttpheaderdialog.ui ) target_include_directories(ProfileEditorPlugin - PRIVATE ${CMAKE_SOURCE_DIR}/plugins - PRIVATE ${CMAKE_SOURCE_DIR}/lib/web + PRIVATE ${CMAKE_SOURCE_DIR}/include ) target_link_libraries(ProfileEditorPlugin PRIVATE Qt5::Widgets PRIVATE Qt5::WebEngineWidgets - PRIVATE web ) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") diff --git a/plugins/ProfileEditor/forms/newprofiledialog.cpp b/plugins/ProfileEditor/forms/newprofiledialog.cpp index 13c46f8..3ed61d8 100644 --- a/plugins/ProfileEditor/forms/newprofiledialog.cpp +++ b/plugins/ProfileEditor/forms/newprofiledialog.cpp @@ -25,3 +25,8 @@ const QString NewProfileDialog::getId() const { return ui->id_lineEdit->text(); } + +bool NewProfileDialog::getOtr() const +{ + return ui->offTheRecord->isChecked(); +} diff --git a/plugins/ProfileEditor/forms/newprofiledialog.h b/plugins/ProfileEditor/forms/newprofiledialog.h index 84626fa..dd9aa4e 100644 --- a/plugins/ProfileEditor/forms/newprofiledialog.h +++ b/plugins/ProfileEditor/forms/newprofiledialog.h @@ -24,6 +24,7 @@ public: ~NewProfileDialog(); const QString getId() const; + bool getOtr() const; private: Ui::NewProfileDialog *ui; diff --git a/plugins/ProfileEditor/forms/newprofiledialog.ui b/plugins/ProfileEditor/forms/newprofiledialog.ui index c46c3b3..8b4888d 100644 --- a/plugins/ProfileEditor/forms/newprofiledialog.ui +++ b/plugins/ProfileEditor/forms/newprofiledialog.ui @@ -7,28 +7,24 @@ <x>0</x> <y>0</y> <width>320</width> - <height>78</height> + <height>112</height> </rect> </property> <property name="windowTitle"> <string>New Profile</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <layout class="QFormLayout" name="formLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="id_label"> - <property name="text"> - <string>ID</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="id_lineEdit"/> - </item> - </layout> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="id_label"> + <property name="text"> + <string>ID</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="id_lineEdit"/> </item> - <item> + <item row="2" column="0" colspan="2"> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -38,6 +34,13 @@ </property> </widget> </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="offTheRecord"> + <property name="text"> + <string>Off the record</string> + </property> + </widget> + </item> </layout> </widget> <resources/> diff --git a/plugins/ProfileEditor/forms/profilemanagerdialog.cpp b/plugins/ProfileEditor/forms/profilemanagerdialog.cpp index b6c1c7e..f5a17b1 100644 --- a/plugins/ProfileEditor/forms/profilemanagerdialog.cpp +++ b/plugins/ProfileEditor/forms/profilemanagerdialog.cpp @@ -8,13 +8,10 @@ #include "profilemanagerdialog.h" #include "profileview.h" -#include "ui_profilemanagerdialog.h" -#include <webprofile.h> #include <QDir> #include <QPointer> -#include "newprofiledialog.h" -ProfileManagerDialog::ProfileManagerDialog(const ProfileManager *profiles, QWidget *parent) +ProfileManagerDialog::ProfileManagerDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::ProfileManagerDialog) { @@ -22,22 +19,6 @@ ProfileManagerDialog::ProfileManagerDialog(const ProfileManager *profiles, QWidg connect(ui->listWidget, &QListWidget::itemPressed, this, &ProfileManagerDialog::showProfile); showProfile(nullptr); - - connect(ui->new_pushButton, &QPushButton::clicked, this, [=]() { - auto *profileDlg = new NewProfileDialog(this); - if(profileDlg->exec() == QDialog::Accepted) { - emit createProfile(profileDlg->getId()); - } - delete profileDlg; - }); - - connect(ui->delete_pushButton, &QPushButton::clicked, this, [=]() { - deleteProfile(ui->listWidget->currentItem()); - }); - - for(const QString &profileId : profiles->idList()) { - addProfile(profiles->profile(profileId)); - } } ProfileManagerDialog::~ProfileManagerDialog() @@ -45,14 +26,13 @@ ProfileManagerDialog::~ProfileManagerDialog() delete ui; } -void ProfileManagerDialog::addProfile(WebProfile *profile) +void ProfileManagerDialog::addProfile(const QString &id, Profile *profile) { Q_CHECK_PTR(profile); - auto *item = new QListWidgetItem(ui->listWidget); - item->setText(profile->name()); + auto *item = new QListWidgetItem(id, ui->listWidget); - auto pointer = QPointer<WebProfile>(profile); + auto pointer = QPointer<Profile>(profile); item->setData(Qt::UserRole, QVariant::fromValue(pointer)); } @@ -71,28 +51,8 @@ void ProfileManagerDialog::showProfile(QListWidgetItem *item) } ui->groupBox->setVisible(true); - auto profile = item->data(Qt::UserRole).value<QPointer<WebProfile>>(); + auto profile = item->data(Qt::UserRole).value<QPointer<Profile>>(); auto *v = new ProfileView(profile.data(), this); ui->groupBox->layout()->addWidget(v); v->adjustSize(); } - -void ProfileManagerDialog::deleteProfile(QListWidgetItem *item) -{ - if(item == nullptr) - return; - - // clear out groupbox layout - QLayoutItem *i; - while((i = ui->groupBox->layout()->takeAt(0)) != nullptr) { - delete i->widget(); - delete i; - } - - auto profile = item->data(Qt::UserRole).value<QPointer<WebProfile>>(); - Q_ASSERT(!profile.isNull()); - - emit removeProfile(profile); - - delete item; -} diff --git a/plugins/ProfileEditor/forms/profilemanagerdialog.h b/plugins/ProfileEditor/forms/profilemanagerdialog.h index 35c2111..fe91948 100644 --- a/plugins/ProfileEditor/forms/profilemanagerdialog.h +++ b/plugins/ProfileEditor/forms/profilemanagerdialog.h @@ -11,12 +11,8 @@ #include <QDialog> #include <QVector> -#include <profilemanager.h> - -namespace Ui -{ -class ProfileManagerDialog; -} +#include <profileinterface.h> +#include "ui_profilemanagerdialog.h" class WebProfile; class QListWidgetItem; @@ -24,20 +20,17 @@ class ProfileManagerDialog : public QDialog { Q_OBJECT + friend class ProfileEditorPlugin; + public: - explicit ProfileManagerDialog(const ProfileManager *profiles, QWidget *parent = 0); + explicit ProfileManagerDialog(QWidget *parent = 0); ~ProfileManagerDialog() override; -signals: - void createProfile(const QString &id); - void removeProfile(const WebProfile *profile); - public slots: - void addProfile(WebProfile *profile); + void addProfile(const QString &id, Profile *profile); private slots: void showProfile(QListWidgetItem *item); - void deleteProfile(QListWidgetItem *item); private: Ui::ProfileManagerDialog *ui; diff --git a/plugins/ProfileEditor/forms/profileview.cpp b/plugins/ProfileEditor/forms/profileview.cpp index 4a89af6..9a19cfc 100644 --- a/plugins/ProfileEditor/forms/profileview.cpp +++ b/plugins/ProfileEditor/forms/profileview.cpp @@ -8,14 +8,13 @@ #include "profileview.h" #include "ui_profileview.h" -#include <webprofile.h> -#include <profilemanager.h> +#include <profileinterface.h> #include <QWebEngineSettings> #include <QWebEngineCookieStore> #include <QDateTime> #include "newhttpheaderdialog.h" -inline void connectSetting(QCheckBox *checkBox, WebProfile *profile, QWebEngineSettings::WebAttribute attr) +inline void connectSetting(QCheckBox *checkBox, Profile *profile, QWebEngineSettings::WebAttribute attr) { checkBox->setChecked(profile->settings()->testAttribute(attr)); QObject::connect(checkBox, &QCheckBox::clicked, [profile, attr](bool checked) { @@ -24,7 +23,7 @@ inline void connectSetting(QCheckBox *checkBox, WebProfile *profile, QWebEngineS }); } -ProfileView::ProfileView(WebProfile *profile, QWidget *parent) +ProfileView::ProfileView(Profile *profile, QWidget *parent) : QWidget(parent) , ui(new Ui::ProfileView) { @@ -92,8 +91,8 @@ ProfileView::ProfileView(WebProfile *profile, QWidget *parent) //ui->httpHeaders->addItem(); headerChanged(i.key(), i.value()); } - connect(m_profile, &WebProfile::headerChanged, this, &ProfileView::headerChanged); - connect(m_profile, &WebProfile::headerRemoved, this, &ProfileView::headerRemoved); + connect(m_profile, &Profile::headerChanged, this, &ProfileView::headerChanged); + connect(m_profile, &Profile::headerRemoved, this, &ProfileView::headerRemoved); connect(ui->headers_insert, &QPushButton::clicked, m_profile, [this]() { auto *dlg = new NewHttpHeaderDialog(this); if(dlg->exec() == QDialog::Accepted) { diff --git a/plugins/ProfileEditor/forms/profileview.h b/plugins/ProfileEditor/forms/profileview.h index d05b0f1..7f3e738 100644 --- a/plugins/ProfileEditor/forms/profileview.h +++ b/plugins/ProfileEditor/forms/profileview.h @@ -18,7 +18,7 @@ namespace Ui class ProfileView; } -class WebProfile; +class Profile; class QWebEngineCookieStore; class QCheckBox; @@ -27,7 +27,7 @@ class ProfileView : public QWidget Q_OBJECT public: - explicit ProfileView(WebProfile *profile, QWidget *parent = nullptr); + explicit ProfileView(Profile *profile, QWidget *parent = nullptr); ~ProfileView() override; private slots: @@ -41,7 +41,7 @@ private slots: private: Ui::ProfileView *ui; - WebProfile *m_profile; + Profile *m_profile; }; #endif // PROFILEDIALOG_H diff --git a/plugins/ProfileEditor/profileeditorplugin.cpp b/plugins/ProfileEditor/profileeditorplugin.cpp index 1eb7b28..d480392 100644 --- a/plugins/ProfileEditor/profileeditorplugin.cpp +++ b/plugins/ProfileEditor/profileeditorplugin.cpp @@ -8,9 +8,7 @@ #include "profileeditorplugin.h" #include "forms/profilemanagerdialog.h" -#include "forms/profileview.h" -#include <QVector> -#include <webprofile.h> +#include "forms/newprofiledialog.h" QHash<QString, std::function<int()>> ProfileEditorPlugin::commands() { @@ -23,20 +21,34 @@ QHash<QString, std::function<int()>> ProfileEditorPlugin::commands() return hash; } -QDialog *ProfileEditorPlugin::createWidget(QWidget *parent) +QDialog *ProfileEditorPlugin::createWidget(QWidget *parent) const { - Q_CHECK_PTR(browser); - auto *widget = new ProfileManagerDialog(browser->getProfileManager(), parent); + auto *app = browser(); + Q_CHECK_PTR(app); + auto *widget = new ProfileManagerDialog(parent); widget->setAttribute(Qt::WA_DeleteOnClose, true); - connect(widget, &ProfileManagerDialog::createProfile, this, [=](const QString &id) { - auto newProfile = browser->loadProfile(id); - widget->addProfile(newProfile.second); + for(const auto &pair : app->profileList()) { + widget->addProfile(pair.first, pair.second); + } + + connect(widget->ui->new_pushButton, &QPushButton::clicked, widget, [=]() { + auto *newProfileDlg = new NewProfileDialog(widget); + if(newProfileDlg->exec() == QDialog::Accepted) { + const auto pair = app->loadProfile(newProfileDlg->getId(), newProfileDlg->getOtr()); + widget->addProfile(pair.first, pair.second); + } + delete newProfileDlg; }); - connect(widget, &ProfileManagerDialog::removeProfile, this, [this](const WebProfile *profile) { - const QString id = browser->getProfileManager()->id(profile); - browser->getProfileManager()->deleteProfile(id); + connect(widget->ui->delete_pushButton, &QPushButton::clicked, widget, [=]() { + auto *item = widget->ui->listWidget->currentItem(); + if(item != nullptr) { + // there is a selected widget + const QString id = item->text(); + widget->showProfile(nullptr); + app->removeProfile(id); + } }); return widget; diff --git a/plugins/ProfileEditor/profileeditorplugin.h b/plugins/ProfileEditor/profileeditorplugin.h index 70f077d..a673436 100644 --- a/plugins/ProfileEditor/profileeditorplugin.h +++ b/plugins/ProfileEditor/profileeditorplugin.h @@ -9,10 +9,8 @@ #ifndef PROFILEEDITOR_PLUGIN_H #define PROFILEEDITOR_PLUGIN_H -#include <QVector> -#include <interfaces.h> +#include <plugininterface.h> -class QWebEngineProfile; class QDialog; class ProfileEditorPlugin : public QObject, public PluginInterface { @@ -25,7 +23,7 @@ public: QHash<QString, std::function<int()>> commands() override; // ProfileInterface - QDialog *createWidget(QWidget *parent) override; + QDialog *createWidget(QWidget *parent) const override; }; #endif //PROFILEEDITOR_PLUGIN_H diff --git a/plugins/interfaces.h b/plugins/interfaces.h deleted file mode 100644 index ea8c9d6..0000000 --- a/plugins/interfaces.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: MIT - */ - -#ifndef SMOLBOTE_PLUGIN_INTERFACES_H -#define SMOLBOTE_PLUGIN_INTERFACES_H - -#include <QtPlugin> -#include <QKeySequence> -#include <memory> -#include <functional> - -class QString; -class QAction; -class QDialog; -class WebProfile; -class Configuration; - -class ProfileManager; -class BrowserInterface -{ -public: - virtual Configuration *getConfiguration() const = 0; - - virtual QPair<QString, WebProfile *> loadProfile(const QString &id) = 0; - virtual ProfileManager *getProfileManager() = 0; -}; - -struct Plugin -{ - QString name; - QString author; - QKeySequence shortcut; - QObject *instance; -}; - -typedef QHash<QString, std::function<int()>> CommandHash_t; - -class PluginInterface -{ -public: - virtual ~PluginInterface() = default; - virtual CommandHash_t commands() = 0; - virtual QDialog *createWidget(QWidget *parent = nullptr) = 0; - - void setBrowserInterface(BrowserInterface *browser) - { - this->browser = browser; - } - -protected: - BrowserInterface *browser = nullptr; -}; - -#define PluginInterfaceIid "net.iserlohn-fortress.smolbote.PluginInterface" -Q_DECLARE_INTERFACE(PluginInterface, PluginInterfaceIid) - -#endif // SMOLBOTE_PLUGIN_INTERFACES_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8f2794..a44a44f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,8 +47,10 @@ set(srclist webengine/webview.cpp webengine/webview.h - # plugin interfaces - plugin.h + # interfaces + ${PROJECT_SOURCE_DIR}/include/plugininterface.h + ${PROJECT_SOURCE_DIR}/include/profileinterface.h + ${PROJECT_SOURCE_DIR}/include/browserinterface.h ) # if you are using a custom build location for breakpad: @@ -64,6 +66,7 @@ endif() target_include_directories(${poi_exe} PRIVATE ${Boost_INCLUDE_DIRS} + PRIVATE ${PROJECT_SOURCE_DIR}/include PRIVATE ${PROJECT_SOURCE_DIR}/lib PRIVATE ${PROJECT_SOURCE_DIR}/plugins ) diff --git a/src/browser.cpp b/src/browser.cpp index 97020bc..0ad1071 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -28,6 +28,8 @@ #include <web/profilemanager.h> #include <web/webprofile.h> #include "webengine/filter.h" +#include <QPluginLoader> +#include <plugininterface.h> Browser::Browser(int &argc, char *argv[], bool allowSecondary) : SingleApplication(argc, argv, allowSecondary, SingleApplication::User | SingleApplication::SecondaryNotification | SingleApplication::ExcludeAppVersion) @@ -49,23 +51,48 @@ Browser::~Browser() void Browser::about() { auto *dlg = new AboutDialog; - for(const Plugin &plugin : qAsConst(m_plugins)) { - dlg->addPlugin(plugin.name, plugin.author, plugin.shortcut.toString()); + for(const QPluginLoader *loader : qAsConst(m_plugins)) { + const auto meta = loader->metaData().value("MetaData").toObject(); + dlg->addPlugin(meta.value("name").toString(), meta.value("author").toString(), meta.value("shortcut").toString()); } dlg->exec(); } -QPair<QString, WebProfile *> Browser::loadProfile(const QString &id) +const QStringList Browser::configurationOptions() const +{ + QStringList options; + for(const auto &option : m_config->options()) { + options.append(QString::fromStdString(option->long_name())); + } + return options; +} + +const QString Browser::configuration(const QString& key) const +{ + return m_config->value<QString>(qUtf8Printable(key)).value_or(QString()); +} + +void Browser::setConfiguration(const QString& key, const QString& value) +{ + m_config->setValue(qUtf8Printable(key), value); +} + +const QList<QPair<QString, Profile *>> Browser::profileList() const +{ + QList<QPair<QString, Profile *>> profiles; + for(const QString &id : m_profileManager->idList()) { + profiles.append(qMakePair(id, m_profileManager->profile(id))); + } + return profiles; +} + +QPair<QString, Profile *> Browser::loadProfile(const QString &id, bool isOffTheRecord) { WebProfile *profile = nullptr; if(QFile::exists(id)) { - //qDebug("id %s exists", qUtf8Printable(id)); profile = m_profileManager->loadProfile(id); } else { - QDir profileDir(m_config->value<QString>("profile.path").value()); - //qDebug("id %s --> %s", qUtf8Printable(id), qUtf8Printable(profileDir.absoluteFilePath(id + ".profile"))); - profile = m_profileManager->loadProfile(profileDir.absoluteFilePath(id + ".profile")); - profile->setName(id); + profile = m_profileManager->createProfile(id, isOffTheRecord); } connect(profile, &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); auto *interceptor = new UrlRequestInterceptor(m_urlFilter.get(), profile, profile); @@ -74,6 +101,11 @@ QPair<QString, WebProfile *> Browser::loadProfile(const QString &id) return QPair<QString, WebProfile *>(m_profileManager->id(profile), profile); } +void Browser::removeProfile(const QString &id) +{ + m_profileManager->deleteProfile(id); +} + void Browser::setConfiguration(std::unique_ptr<Configuration> &config) { Q_ASSERT(config); @@ -91,20 +123,10 @@ ProfileManager *Browser::getProfileManager() return m_profileManager; } -void Browser::registerPlugin(const Plugin &plugin) -{ - Q_ASSERT(m_config); - - auto *p = qobject_cast<PluginInterface *>(plugin.instance); - if(p != nullptr) { - p->setBrowserInterface(this); - m_plugins.append(plugin); - } -} - -void Browser::setup() +void Browser::setup(QVector<QPluginLoader *> plugins) { Q_ASSERT(m_config); + m_plugins = plugins; auto stylesheet = m_config->value<QString>("browser.stylesheet"); if(stylesheet) { @@ -132,7 +154,7 @@ void Browser::setup() const QString id = m_config->value<QString>("profile.default").value(); auto *profile = m_profileManager->profile(id); if(profile == nullptr) { - profile = this->loadProfile(id).second; + profile = qobject_cast<WebProfile *>(loadProfile(id).second); } WebProfile::setDefaultProfile(profile); @@ -225,13 +247,12 @@ MainWindow *Browser::createWindow() }); window->addAction(MainWindow::ToolsMenu, downloadsAction); - for(const Plugin &p : qAsConst(m_plugins)) { - auto *plugin = qobject_cast<PluginInterface *>(p.instance); + for(QPluginLoader *loader : qAsConst(m_plugins)) { + const auto *plugin = qobject_cast<PluginInterface *>(loader->instance()); Q_CHECK_PTR(plugin); - auto *pluginAction = new QAction(p.name, window); - pluginAction->setShortcut(p.shortcut); - + auto *pluginAction = new QAction(loader->metaData().value("MetaData").toObject().value("name").toString(), window); + pluginAction->setShortcut(QKeySequence::fromString(loader->metaData().value("MetaData").toObject().value("shortcut").toString())); connect(pluginAction, &QAction::triggered, window, [=]() { plugin->createWidget(window)->exec(); }); diff --git a/src/browser.h b/src/browser.h index 6731f59..35b4149 100644 --- a/src/browser.h +++ b/src/browser.h @@ -10,10 +10,10 @@ #define SMOLBOTE_BROWSER_H #include <singleapplication.h> +#include <browserinterface.h> #include <QJsonObject> #include <QVector> #include <functional> -#include <interfaces.h> #include <memory> #include "session.h" @@ -22,7 +22,8 @@ class BookmarksWidget; class DownloadsWidget; class Filter; class MainWindow; -class WebProfile; +class ProfileManager; +class QPluginLoader; class Browser : public SingleApplication, public BrowserInterface { Q_OBJECT @@ -36,16 +37,19 @@ public slots: public: // interface - Configuration *getConfiguration() const override; + Configuration *getConfiguration() const; + const QStringList configurationOptions() const override; + const QString configuration(const QString &key) const override; + void setConfiguration(const QString &key, const QString &value) override; - ProfileManager *getProfileManager() override; - QPair<QString, WebProfile *> loadProfile(const QString &id) override; + ProfileManager *getProfileManager(); + const QList<QPair<QString, Profile *>> profileList() const override; + QPair<QString, Profile *> loadProfile(const QString &id, bool isOffTheRecord = true) override; + void removeProfile(const QString &id) override; void setConfiguration(std::unique_ptr<Configuration> &config); - void registerPlugin(const Plugin &plugin); - - void setup(); + void setup(QVector<QPluginLoader *> plugins); QJsonObject session() const { return Session::session(m_windows); @@ -65,7 +69,7 @@ private: std::unique_ptr<Filter> m_urlFilter; QVector<MainWindow *> m_windows; - QVector<Plugin> m_plugins; + QVector<QPluginLoader *> m_plugins; }; #endif // SMOLBOTE_BROWSER_H diff --git a/src/main.cpp b/src/main.cpp index 6940856..e0ba7eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,6 @@ */ #include "browser.h" -#include "plugin.h" #include "session.h" #include "util.h" #include "version.h" @@ -17,6 +16,8 @@ #include <configuration/configuration.h> #include <iostream> #include <memory> +#include <QPluginLoader> +#include <plugininterface.h> #ifdef _WIN32 #include <cstdio> #include <windows.h> @@ -102,19 +103,23 @@ int main(int argc, char **argv) return 0; } - QVector<Plugin> plugins; + QVector<QPluginLoader *> plugins; + CommandHash_t pluginCommands; + for(const QString &path : Util::files(config->value<QString>("plugins.path").value())) { - plugins.append(loadPluginFromPath(path)); - } - QMap<QString, std::function<int()>> pluginCommands; - for(const auto &plugin : plugins) { - auto *pluginInterface = qobject_cast<PluginInterface *>(plugin.instance); - Q_CHECK_PTR(pluginInterface); - - QHashIterator<QString, std::function<int()>> i(pluginInterface->commands()); - while(i.hasNext()) { - i.next(); - pluginCommands.insert(i.key(), i.value()); + QPluginLoader *loader = new QPluginLoader(path); + const bool loaded = loader->load(); +#ifdef QT_DEBUG + qDebug("Loading plugin %s %s", qUtf8Printable(path), loaded ? "[ok]" : "[failed]"); +#endif + + if(loader->load()) { + plugins.append(loader); + auto *plugin = qobject_cast<PluginInterface *>(loader->instance()); + pluginCommands.unite(plugin->commands()); + } else { + qDebug("%s", qUtf8Printable(loader->errorString())); + delete loader; } } @@ -187,11 +192,7 @@ int main(int argc, char **argv) auto profile = config->value<QString>("profile.default"); app.setConfiguration(config); - app.setup(); - - for(const Plugin &plugin : plugins) { - app.registerPlugin(plugin); - } + app.setup(plugins); QStringList urls; if(arguments) { diff --git a/src/plugin.h b/src/plugin.h deleted file mode 100644 index 045d047..0000000 --- a/src/plugin.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#ifndef SMOLBOTE_PLUGIN_H -#define SMOLBOTE_PLUGIN_H - -#include <interfaces.h> -#include <QPluginLoader> -#include <QFileInfo> -#include <QDir> - -inline Plugin loadPluginFromPath(const QString &path) -{ - Plugin p; - QPluginLoader loader(path); - - if(loader.load()) { -#ifdef QT_DEBUG - qDebug("Loading plugin: %s [ok]", qUtf8Printable(path)); -#endif - - auto meta = loader.metaData().value("MetaData").toObject(); - p.name = meta.value("name").toString(); - p.author = meta.value("author").toString(); - p.shortcut = QKeySequence::fromString(meta.value("shortcut").toString()); - - p.instance = loader.instance(); - - } else { - qDebug("Loading pluing: %s [failed]", qUtf8Printable(path)); - qDebug("%s", qUtf8Printable(loader.errorString())); - } - - return p; -} - -#endif // SMOLBOTE_PLUGIN_H |