aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/man/smolbote.1.scd3
-rw-r--r--meson.build6
-rw-r--r--src/bookmarks/builtin.cpp108
-rw-r--r--src/bookmarks/builtins.cpp105
-rw-r--r--src/bookmarks/meson.build8
-rw-r--r--src/browser.cpp112
-rw-r--r--src/browser.h20
-rw-r--r--src/builtins.cpp55
-rw-r--r--src/builtins.h28
-rw-r--r--src/cmd/cmd.hpp89
-rw-r--r--src/cmd/meson.build2
-rw-r--r--src/cmd/test.cpp41
-rw-r--r--src/configuration/builtin.cpp41
-rw-r--r--src/configuration/meson.build1
-rw-r--r--src/main.cpp160
-rw-r--r--src/meson.build12
-rw-r--r--src/session/builtin.cpp61
-rw-r--r--src/session/meson.build1
-rw-r--r--subprojects/args.wrap6
-rw-r--r--test/cli/cli.cpp92
-rw-r--r--test/cli/meson.build4
21 files changed, 476 insertions, 479 deletions
diff --git a/doc/man/smolbote.1.scd b/doc/man/smolbote.1.scd
index 758fda3..2cdc1ba 100644
--- a/doc/man/smolbote.1.scd
+++ b/doc/man/smolbote.1.scd
@@ -23,6 +23,9 @@ smolbote is a cross-platform keep-it-simple free software web browser that uses
- `-s, --session`: Open the selected session.
- `--pick-session`: Open all available sessions and select which one to open.
+## Env
+- 'POI_LOG_PATTERN'
+
# SEE ALSO
*smolboterc*(5) - configuration file and options++
diff --git a/meson.build b/meson.build
index 1dd1a1f..272cb60 100644
--- a/meson.build
+++ b/meson.build
@@ -55,7 +55,7 @@ add_project_arguments(cxx.get_supported_arguments([
'-Wshadow', # if variable declaration shadows one from a parent context
# functions
'-Wnon-virtual-dtor', # if class with virtual functions has non-virtual dtor
- '-Werror=missing-declarations', # missing function declarations in header files
+ '-Wmissing-declarations', # missing function declarations in header files
'-Werror=redundant-decls',
'-Woverloaded-virtual', # warn if you overload (not override) a virtual function
'-Werror=return-type',
@@ -95,7 +95,6 @@ dep_spdlog = dependency('spdlog', fallback: ['spdlog', 'spdlog_dep'], version: '
dep_gtest = dependency('gtest', required: false, disabler: true)
dep_catch = dependency('catch2', required: true, fallback: ['catch2', 'catch2_dep'] )
dep_SingleApplication = dependency('singleapplication', fallback: [ 'singleapplication', 'SingleApplication_dep' ])
-dep_args = dependency('args.hxx', fallback: [ 'args', 'args_dep' ])
# Generate config header
poi_sourceset = sourceset.source_set()
@@ -113,7 +112,6 @@ subdir('lib/smolblok')
subdir('src')
subdir('lang')
subdir('doc')
-subdir('tools')
subdir('plugins/ProfileEditor')
subdir('plugins/HostlistFilter')
@@ -127,7 +125,7 @@ poi_exe = executable(get_option('poi'),
cpp_args: ['-DQAPPLICATION_CLASS=QApplication'],
sources: [ssconfig.sources()],
include_directories: [ plugininterfaces_include, include_directories('src') ],
- dependencies: [ dep_qt5, dep_spdlog, dep_SingleApplication, dep_args, dep_bookmarks, dep_configuration, dep_downloads, dep_pluginloader, ssconfig.dependencies(), lib_session_formats ],
+ dependencies: [ dep_qt5, dep_spdlog, dep_SingleApplication, dep_bookmarks, dep_configuration, dep_downloads, dep_pluginloader, ssconfig.dependencies(), lib_session_formats ],
install: true,
)
diff --git a/src/bookmarks/builtin.cpp b/src/bookmarks/builtin.cpp
new file mode 100644
index 0000000..732aa64
--- /dev/null
+++ b/src/bookmarks/builtin.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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/cgit/smolbote
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "bookmarkmodel.h"
+#include "browser.h"
+#include "configuration.h"
+#include <QBuffer>
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QFile>
+#include <cstdlib>
+#include <iostream>
+#include <spdlog/spdlog.h>
+
+template <auto T>
+[[nodiscard]] inline bool import(BookmarkModel *model, const QString &path)
+{
+ QFile f(path);
+ if(f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ BookmarkFormat<T>(&f) >> model;
+ f.close();
+ return true;
+ }
+ return false;
+}
+
+namespace builtins
+{
+int sub_bookmarks(const QStringList &l, Browser &)
+{
+ const QCommandLineOption output({ "o", "output" }, "Output location (default: append to browser bookmarks).", "file");
+
+ const QCommandLineOption import_xbel({ "x", "import-xbel" }, "Import xbel format.", "xbel");
+ const QCommandLineOption import_json({ "j", "import-json" }, "Import json format.", "json");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("smolbote: bookmarks");
+ parser.addHelpOption();
+
+ parser.addOptions({ output, import_xbel, import_json });
+
+ if(l.count() <= 1) {
+ parser.showHelp();
+ }
+
+ parser.process(l);
+ Configuration conf;
+ const auto bookmarks_path = conf.value<QString>("bookmarks.path").value();
+ auto *model = new BookmarkModel;
+
+ // implicit bookmarks.path import
+ if(!parser.isSet(output)) {
+ if(!import<XbelFormat>(model, bookmarks_path)) {
+ spdlog::error("Could not open %s", qUtf8Printable(bookmarks_path));
+ }
+ }
+
+ for(const auto &i : parser.values(import_xbel)) {
+ if(!import<XbelFormat>(model, i)) {
+ spdlog::error("Could not open %s", qUtf8Printable(i));
+ }
+ }
+
+ for(const auto &i : parser.values(import_json)) {
+ if(!import<FirefoxJsonFormat>(model, i)) {
+ spdlog::error("Could not open %s", qUtf8Printable(i));
+ }
+ }
+
+ QIODevice *out = nullptr;
+ if(!parser.isSet(output)) {
+ out = new QFile(bookmarks_path);
+ } else {
+ const auto o = parser.value(output);
+
+ if(o == "stdout") {
+ out = new QBuffer;
+ QObject::connect(out, &QIODevice::aboutToClose, [out]() {
+ out->seek(0);
+ std::cout << qUtf8Printable(out->readAll()) << std::endl;
+ });
+
+ } else {
+ out = new QFile(o);
+ }
+ }
+
+ if(!out->isOpen()) {
+ if(!out->open(QIODevice::ReadWrite | QIODevice::Text)) {
+ spdlog::error("Could not open output");
+ return EXIT_FAILURE;
+ }
+ }
+
+ BookmarkFormat<XbelFormat> format(out);
+ format << model;
+ out->close();
+
+ delete out;
+ delete model;
+ return EXIT_FAILURE;
+}
+} // namespace
diff --git a/src/bookmarks/builtins.cpp b/src/bookmarks/builtins.cpp
deleted file mode 100644
index f7b6f3f..0000000
--- a/src/bookmarks/builtins.cpp
+++ /dev/null
@@ -1,105 +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
- */
-
-#include "builtins.h"
-#include "bookmarkmodel.h"
-#include "configuration.h"
-#include <QBuffer>
-#include <QFile>
-#include <iostream>
-#include <spdlog/spdlog.h>
-
-int builtins::bookmarks(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs)
-{
- args::ArgumentParser parser("bookmarks",
- "If an output location is not specified, this command will append imported bookmarks to the browser's bookmarks.\n"
- "If an output location is specified, this command will load all XBEL-format bookmarks, then all JSON-format bookmarks, and write them to the output location.");
- parser.Prog(progname);
-
- args::HelpFlag help(parser, "help", "Display this help message and exit.", { 'h', "help" });
- args::ValueFlag<std::string> save(parser, "file, stdout", "Output location.", { 'e', "export" });
- args::ValueFlagList<std::string> import_xbel(parser, "bookmarks.xbel", "Import xbel format.", { 'x', "import-xbel" });
- args::ValueFlagList<std::string> import_json(parser, "bookmarks.json", "Import json format.", { 'j', "import-json" });
-
- try {
- parser.ParseArgs(beginargs, endargs);
- } catch(args::Help &) {
- std::cout << parser;
- return 0;
- } catch(args::Error &e) {
- std::cerr << e.what() << std::endl;
- std::cerr << parser;
- return -1;
- }
-
- auto *model = new BookmarkModel;
-
- // implicit bookmarks.path import
- if(!save) {
- Configuration conf;
- QFile f(conf.value<QString>("bookmarks.path").value());
- if(f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- BookmarkFormat<XbelFormat>(&f) >> model;
- f.close();
- }
- }
-
- for(const auto &i : args::get(import_xbel)) {
- QFile f(QString::fromStdString(i));
- if(f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- BookmarkFormat<XbelFormat>(&f) >> model;
- f.close();
- } else {
- spdlog::error("Could not open %s", i);
- return -1;
- }
- }
-
- for(const auto &i : args::get(import_json)) {
- QFile f(QString::fromStdString(i));
- if(f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- BookmarkFormat<FirefoxJsonFormat>(&f) >> model;
- f.close();
- } else {
- spdlog::error("Could not open %s", i);
- return -1;
- }
- }
-
- QIODevice *output = nullptr;
- if(save && args::get(save) == "stdout") {
- output = new QBuffer;
- QObject::connect(output, &QIODevice::aboutToClose, [output]() {
- output->seek(0);
- std::cout << qUtf8Printable(output->readAll()) << std::endl;
- });
-
- output->open(QIODevice::ReadWrite | QIODevice::Text);
-
- } else if(!save) {
- Configuration conf;
- output = new QFile(conf.value<QString>("bookmarks.path").value());
- } else {
- output = new QFile(QString::fromStdString(args::get(save)));
- }
-
- if(!output->isOpen()) {
- if(!output->open(QIODevice::ReadWrite | QIODevice::Text)) {
- spdlog::error("Could not open output");
- return -1;
- }
- }
-
- BookmarkFormat<XbelFormat> format(output);
- format << model;
- output->close();
-
- delete output;
-
- return 0;
-}
diff --git a/src/bookmarks/meson.build b/src/bookmarks/meson.build
new file mode 100644
index 0000000..8fc93f0
--- /dev/null
+++ b/src/bookmarks/meson.build
@@ -0,0 +1,8 @@
+poi_sourceset.add(files('builtin.cpp',
+ 'bookmarkswidget.cpp', 'editbookmarkdialog.cpp', 'bookmarkstoolbar.cpp'),
+mod_qt5.preprocess(
+ moc_headers: ['bookmarkswidget.h', 'editbookmarkdialog.h'],
+ ui_files: ['bookmarksform.ui', 'editbookmarkdialog.ui'],
+ dependencies: dep_qt5
+))
+
diff --git a/src/browser.cpp b/src/browser.cpp
index aec3ad1..894216e 100644
--- a/src/browser.cpp
+++ b/src/browser.cpp
@@ -1,7 +1,7 @@
/*
* 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
+ * location: https://neueland.iserlohn-fortress.net/cgit/smolbote
*
* SPDX-License-Identifier: GPL-3.0
*/
@@ -16,6 +16,8 @@
#include "mainwindow/addressbar.h"
#include "mainwindow/mainwindow.h"
#include "mainwindow/menubar.h"
+#include "session_json.hpp"
+#include "settings.h"
#include "smolbote/plugininterface.hpp"
#include "subwindow/subwindow.h"
#include "util.h"
@@ -35,13 +37,46 @@
Browser::Browser(int &argc, char *argv[], bool allowSecondary)
: SingleApplication(argc, argv, allowSecondary, SingleApplication::User | SingleApplication::SecondaryNotification | SingleApplication::ExcludeAppVersion)
{
- Configuration conf;
-
- setApplicationName(conf.value<QString>("poi.name").value());
+ //setApplicationName(conf.value<QString>("poi.name").value());
+ setApplicationName("smolbote");
setWindowIcon(Util::icon<Util::AppWindowIcon>());
setApplicationVersion(QVersionNumber::fromString(QLatin1String(poi_Version)).toString());
+}
+
+Browser::~Browser()
+{
+ if(m_bookmarks)
+ m_bookmarks->save();
+
+ for(auto *info : qAsConst(m_plugins))
+ delete info;
+
+ qDeleteAll(m_windows);
+ m_windows.clear();
+}
+
+void Browser::about()
+{
+ auto *dlg = new AboutDialog(activeWindow());
+ dlg->show();
+}
- if(const auto _translation = conf.value<QString>("browser.translation")) {
+void Browser::aboutPlugins()
+{
+ auto *dlg = new AboutPluginDialog;
+ for(auto *info : qAsConst(m_plugins)) {
+ dlg->add(info->loader);
+ }
+ dlg->exec();
+}
+
+void Browser::loadConfiguration(const QString &path)
+{
+ auto ctx = init_conf(path.toStdString());
+ spdlog::info("Using configuration [{}]: {}", ctx.path, ctx.ptr->make_global() ? "okay" : "failed");
+ m_conf = std::move(ctx.ptr);
+
+ if(const auto _translation = m_conf->value<QString>("browser.translation")) {
auto *translator = new QTranslator(this);
if(translator->load(_translation.value()))
installTranslator(translator);
@@ -49,7 +84,7 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary)
delete translator;
}
- if(const auto _locale = conf.value<QString>("browser.locale")) {
+ if(const auto _locale = m_conf->value<QString>("browser.locale")) {
auto *locale = new QTranslator(this);
if(locale->load("qt_" + _locale.value(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
installTranslator(locale);
@@ -57,11 +92,11 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary)
delete locale;
}
- if(auto iconTheme = conf.value<QString>("browser.iconTheme")) {
+ if(auto iconTheme = m_conf->value<QString>("browser.iconTheme")) {
QIcon::setThemeName(iconTheme.value());
}
- if(auto stylesheet = conf.value<QString>("browser.stylesheet")) {
+ if(auto stylesheet = m_conf->value<QString>("browser.stylesheet")) {
QFile f(stylesheet.value());
if(f.open(QIODevice::ReadOnly)) {
setStyleSheet(f.readAll());
@@ -70,20 +105,20 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary)
}
// content filter - register format plugins
- if(const auto hostlist_plugin = conf.value<QString>("smolblok.plugins.hostlist")) {
+ if(const auto hostlist_plugin = m_conf->value<QString>("smolblok.plugins.hostlist")) {
content_filter.registerFormatPlugin("hostlist", hostlist_plugin.value());
}
- if(const auto adblock_plugin = conf.value<QString>("smolblok.plugins.adblock")) {
+ if(const auto adblock_plugin = m_conf->value<QString>("smolblok.plugins.adblock")) {
content_filter.registerFormatPlugin("adblock", adblock_plugin.value());
}
// load profiles
{
- const auto profiles = Util::files(conf.value<QString>("profile.path").value(), { "*.profile" });
- const auto search = conf.value<QString>("profile.search").value();
- const auto homepage = QUrl::fromUserInput(conf.value<QString>("profile.homepage").value());
- const auto newtab = QUrl::fromUserInput(conf.value<QString>("profile.newtab").value());
- const auto default_id = conf.value<QString>("profile.default").value();
+ const auto profiles = Util::files(m_conf->value<QString>("profile.path").value(), { "*.profile" });
+ const auto search = m_conf->value<QString>("profile.search").value();
+ const auto homepage = QUrl::fromUserInput(m_conf->value<QString>("profile.homepage").value());
+ const auto newtab = QUrl::fromUserInput(m_conf->value<QString>("profile.newtab").value());
+ const auto default_id = m_conf->value<QString>("profile.default").value();
m_profileManager = std::make_unique<WebProfileManager<false>>(profiles, default_id, search, homepage, newtab);
m_profileManager->make_global();
@@ -97,14 +132,14 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary)
}
// downloads
- m_downloads = std::make_unique<DownloadsWidget>(conf.value<QString>("downloads.path").value());
+ m_downloads = std::make_unique<DownloadsWidget>(m_conf->value<QString>("downloads.path").value());
m_profileManager->walk([this](const QString &, WebProfile *profile, QSettings *) {
profile->setUrlRequestInterceptor(content_filter.interceptor());
connect(profile, &QWebEngineProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload);
});
// bookmarks
- m_bookmarks = std::make_shared<BookmarksWidget>(QString::fromStdString(conf.value<std::string>("bookmarks.path").value()));
+ m_bookmarks = std::make_shared<BookmarksWidget>(m_conf->value<QString>("bookmarks.path").value());
connect(m_bookmarks.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) {
m_windows.last()->createTab(url);
});
@@ -114,33 +149,6 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary)
timer->start(5 * 60 * 1000); // 5min * 60sec * 1000ms
}
-Browser::~Browser()
-{
- if(m_bookmarks)
- m_bookmarks->save();
-
- for(auto *info : qAsConst(m_plugins))
- delete info;
-
- qDeleteAll(m_windows);
- m_windows.clear();
-}
-
-void Browser::about()
-{
- auto *dlg = new AboutDialog(activeWindow());
- dlg->show();
-}
-
-void Browser::aboutPlugins()
-{
- auto *dlg = new AboutPluginDialog;
- for(auto *info : qAsConst(m_plugins)) {
- dlg->add(info->loader);
- }
- dlg->exec();
-}
-
bool Browser::loadPlugin(const QString &path)
{
if(path.isEmpty()) {
@@ -167,6 +175,22 @@ bool Browser::loadPlugin(const QString &path)
return true;
}
+void Browser::enableRemote(bool toggle)
+{
+ if(static_cast<bool>(remoteConnection) == toggle)
+ return;
+
+ if(!toggle) {
+ disconnect(remoteConnection);
+ return;
+ }
+
+ remoteConnection = connect(this, &Browser::receivedMessage, this, [this](quint32, const QByteArray &message) {
+ JsonSession s(message);
+ this->open(s.get());
+ });
+}
+
void Browser::showWidget(QWidget *widget, MainWindow *where) const
{
bool wasVisible = widget->isVisible();
diff --git a/src/browser.h b/src/browser.h
index 69cf804..8e443fe 100644
--- a/src/browser.h
+++ b/src/browser.h
@@ -1,7 +1,7 @@
/*
* 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
+ * location: https://neueland.iserlohn-fortress.net/cgit/smolbote
*
* SPDX-License-Identifier: GPL-3.0
*/
@@ -37,26 +37,32 @@ public:
return qAsConst(m_windows);
}
- BookmarksWidget *bookmarks() const
+ [[deprecated]] BookmarksWidget *bookmarks() const
{
return m_bookmarks.get();
}
- DownloadsWidget *downloads() const
+ [[deprecated]] DownloadsWidget *downloads() const
{
return m_downloads.get();
}
+ bool remoteEnabled() const
+ {
+ return static_cast<bool>(remoteConnection);
+ }
signals:
void pluginAdded(QPluginLoader *);
public slots:
- void about();
- void aboutPlugins();
+ [[deprecated]] void about();
+ [[deprecated]] void aboutPlugins();
- void showWidget(QWidget *widget, MainWindow *where) const;
+ [[deprecated]] void showWidget(QWidget *widget, MainWindow *where) const;
void open(const QVector<Session::MainWindow> &data, bool merge = true);
+ void loadConfiguration(const QString &path);
bool loadPlugin(const QString &path);
+ void enableRemote(bool toggle);
private:
struct PluginInfo {
@@ -77,6 +83,8 @@ private:
Q_DISABLE_COPY(Browser)
+ QMetaObject::Connection remoteConnection;
+ std::unique_ptr<Configuration> m_conf;
std::shared_ptr<BookmarksWidget> m_bookmarks;
std::unique_ptr<DownloadsWidget> m_downloads;
std::unique_ptr<WebProfileManager<false>> m_profileManager{ nullptr };
diff --git a/src/builtins.cpp b/src/builtins.cpp
deleted file mode 100644
index ab5942c..0000000
--- a/src/builtins.cpp
+++ /dev/null
@@ -1,55 +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
- */
-
-#include "builtins.h"
-#include "configuration.h"
-#include "version.h"
-#include <QObject>
-#include <QVersionNumber>
-#include <iostream>
-
-int builtins::version()
-{
- const auto version = QVersionNumber::fromString(QLatin1String(poi_Version)).toString().toStdString();
- std::cout << "smolbote " << version << std::endl;
- return 0;
-}
-
-int builtins::build()
-{
- std::cout << poi_Version << std::endl;
- return 0;
-}
-
-int builtins::configuration(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs)
-{
- args::ArgumentParser parser("configuration");
- parser.Prog(progname);
-
- args::HelpFlag help(parser, "help", "Display this help message and exit.", { 'h', "help" });
- args::Flag dump(parser, "dump", "Dump currently used configuration and exit", { "dump" });
-
- try {
- parser.ParseArgs(beginargs, endargs);
- } catch(args::Help &e) {
- std::cout << parser;
- return 0;
- } catch(args::Error &e) {
- std::cerr << e.what() << std::endl;
- std::cerr << parser;
- return -1;
- }
-
- if(dump) {
- Configuration conf;
- std::cout << conf << std::endl;
- return 0;
- }
-
- return 0;
-}
diff --git a/src/builtins.h b/src/builtins.h
deleted file mode 100644
index 9fdca98..0000000
--- a/src/builtins.h
+++ /dev/null
@@ -1,28 +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_BUILTINS_H
-#define SMOLBOTE_BUILTINS_H
-
-#include "smolbote/plugininterface.hpp"
-#include <args.hxx>
-
-typedef std::function<int(const std::string &, std::vector<std::string>::const_iterator, std::vector<std::string>::const_iterator)> subcommand_func;
-typedef std::unordered_map<std::string, subcommand_func> command_map;
-
-namespace builtins
-{
-int version();
-int build();
-
-int configuration(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs);
-
-int bookmarks(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs);
-}
-
-#endif
diff --git a/src/cmd/cmd.hpp b/src/cmd/cmd.hpp
new file mode 100644
index 0000000..37bb3ce
--- /dev/null
+++ b/src/cmd/cmd.hpp
@@ -0,0 +1,89 @@
+/*
+ * 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/cgit/smolbote
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#ifndef SMOLBOTE_CMD_HPP
+#define SMOLBOTE_CMD_HPP
+
+#include "version.h"
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <functional>
+#include <iostream>
+
+namespace command_line
+{
+template <typename T>
+using subcommand_fn = std::function<int(const QStringList &, T &)>;
+template <typename T>
+using map = std::unordered_map<std::string, subcommand_fn<T>>;
+
+// a helper function to join the keys of a command_map into a string
+template <typename T>
+[[nodiscard]] inline QString join_keys(const map<T> &map, const QString sep = ", ")
+{
+ QString k;
+ for(auto it = map.cbegin(); it != map.cend(); ++it) {
+ k += QString::fromStdString(it->first);
+ if(std::next(it) != map.cend()) {
+ k += sep;
+ }
+ }
+ return k;
+}
+
+template <typename T>
+[[nodiscard]] std::function<int(T &)> process(T &app, const map<T> &map, const std::string &d)
+{
+ const QCommandLineOption build({ "b", "build" }, "Display build information.");
+ const QCommandLineOption config({ "c", "config" }, "Set the configuration file.", "file");
+ const QCommandLineOption noRemote("no-remote", "Do not accept or send remote commands.");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("smolbote: yet another no-frills browser");
+ parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.addOption(build);
+ parser.addOption(config);
+ parser.addOption(noRemote);
+
+ parser.addPositionalArgument("subcommand", QString("%1; default: %2").arg(join_keys(map), QString::fromStdString(d)));
+ parser.addPositionalArgument("urls", "List of URLs to open.");
+
+ parser.process(app);
+
+ if(parser.isSet(build)) {
+ std::cout << app.applicationName().toStdString() << " " << poi_Version << std::endl;
+ exit(0);
+ }
+
+ if constexpr(requires { app.loadConfiguration(parser.value(config)); }) {
+ app.loadConfiguration(parser.value(config));
+ } else {
+ qDebug("warning: cannot init configuration");
+ }
+
+ if constexpr(requires { app.enableRemote(true); }) {
+ app.enableRemote(!parser.isSet(noRemote));
+ }
+
+ const auto args = parser.positionalArguments();
+
+ if(args.count() >= 1) {
+ auto i = map.find(args.first().toStdString());
+ if(i != map.end())
+ return std::bind(i->second, args, std::placeholders::_1);
+ }
+
+ return std::bind(map.at(d), args, std::placeholders::_1);
+}
+
+} // namespace
+
+#endif
diff --git a/src/cmd/meson.build b/src/cmd/meson.build
new file mode 100644
index 0000000..466647f
--- /dev/null
+++ b/src/cmd/meson.build
@@ -0,0 +1,2 @@
+test('command line parser', executable('cmd_test', ['test.cpp', version_h], dependencies: [ dep_qt5 ]))
+
diff --git a/src/cmd/test.cpp b/src/cmd/test.cpp
new file mode 100644
index 0000000..fc102ff
--- /dev/null
+++ b/src/cmd/test.cpp
@@ -0,0 +1,41 @@
+#include "cmd.hpp"
+#include <QCoreApplication>
+
+int sub_bookmarks(const QStringList &l, QCoreApplication &)
+{
+ const QCommandLineOption q("q", "something");
+ QCommandLineParser parser;
+ parser.setApplicationDescription("testing bookmarks editor");
+ parser.addHelpOption();
+ parser.addOption(q);
+
+ if(l.count() <= 1) {
+ parser.showHelp();
+ }
+ parser.process(l);
+ return 0;
+}
+
+int sub_list(const QStringList &args, QCoreApplication &)
+{
+ for(const auto &x : args) {
+ qDebug("-%s", qUtf8Printable(x));
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationName("cmd_test");
+ app.setApplicationVersion("0.1.0");
+
+ const command_line::map<QCoreApplication> m{
+ { "bookmarks", &sub_bookmarks },
+ { "list", &sub_list }
+ };
+
+ const auto f = command_line::process<QCoreApplication>(app, m, "list");
+ return f(app);
+}
+
diff --git a/src/configuration/builtin.cpp b/src/configuration/builtin.cpp
new file mode 100644
index 0000000..a6ed002
--- /dev/null
+++ b/src/configuration/builtin.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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/cgit/smolbote
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "browser.h"
+#include "configuration.h"
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <cstdlib>
+#include <iostream>
+
+namespace builtins
+{
+int sub_configuration(const QStringList &l, Browser &)
+{
+ const QCommandLineOption dump({ "d", "dump" }, "Print the currently used configuration and exit.");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("configuration");
+ parser.addHelpOption();
+ parser.addOption(dump);
+
+ if(l.count() <= 1) {
+ parser.showHelp();
+ }
+
+ parser.process(l);
+
+ if(parser.isSet(dump)) {
+ Configuration conf;
+ std::cout << conf << std::endl;
+ return EXIT_SUCCESS;
+ }
+
+ return EXIT_FAILURE;
+}
+}
diff --git a/src/configuration/meson.build b/src/configuration/meson.build
new file mode 100644
index 0000000..5ba8ffd
--- /dev/null
+++ b/src/configuration/meson.build
@@ -0,0 +1 @@
+poi_sourceset.add(files('builtin.cpp'))
diff --git a/src/main.cpp b/src/main.cpp
index 97b529b..88fa657 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,162 +1,66 @@
/*
* 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
+ * location: https://neueland.iserlohn-fortress.net/cgit/smolbote
*
* SPDX-License-Identifier: GPL-3.0
*/
#include "browser.h"
-#include "builtins.h"
+#include "cmd/cmd.hpp"
#include "configuration.h"
-#include "session/sessiondialog.h"
-#include "session_json.hpp"
-#include "settings.h"
#include "util.h"
-#include "version.h"
#include <QFile>
#include <QPluginLoader>
#include <QStandardPaths>
-#include <iostream>
-#include <memory>
#include <pluginloader.h>
#include <spdlog/spdlog.h>
-// a helper function to join the keys of a command_map into a string
-inline std::string join_keys(const command_map &map, const std::string sep = ", ")
+namespace builtins
{
- std::vector<std::string> keys(map.size());
- std::transform(map.begin(), map.end(), keys.begin(), [](auto pair) { return pair.first; });
- std::sort(keys.begin(), keys.end());
-
- std::string k;
- std::for_each(keys.begin(), keys.end() - 1, [&k, &sep](const std::string &piece) { k += piece + sep; });
- k += keys.back();
-
- return k;
+int sub_configuration(const QStringList &l, Browser &);
+int sub_bookmarks(const QStringList &l, Browser &);
+int sub_session(const QStringList &l, Browser &);
}
int main(int argc, char **argv)
{
- // change log pattern
- spdlog::set_pattern("[%^%l%$] [%P:%t] %v");
+ // spdlog pattern
+ if(const char *env_spdlog = std::getenv("POI_LOG_PATTERN"))
+ spdlog::set_pattern(env_spdlog);
+ else
+ spdlog::set_pattern("[%^%l%$] [%P:%t] %v");
+
#ifdef QT_DEBUG
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
#endif
- const command_map commands{
- { "configuration", builtins::configuration },
- { "bookmarks", builtins::bookmarks },
+ const command_line::map<Browser> subcommands{
+ { "configuration", builtins::sub_configuration },
+ { "bookmarks", builtins::sub_bookmarks },
+ { "session", builtins::sub_session },
};
- const std::vector<std::string> args(argv + 1, argv + argc);
- args::ArgumentParser parser("smolbote: yet another no-frills browser", "Subcommands: " + join_keys(commands));
- parser.Prog(argv[0]);
-
- args::HelpFlag cmd_help(parser, "help", "Display this help message.", { 'h', "help" });
- args::Flag cmd_version(parser, "version", "Display version information.", { 'v', "version" });
- args::Flag cmd_build(parser, "build", "Display build commit.", { 'b', "build" });
-
- args::ValueFlag<std::string> cmd_config(parser, "config", "Set the configuration file.", { 'c', "config" });
-
- args::Flag cmd_noRemote(parser, "no-remote", "Do not accept or send remote commands.", { "no-remote" });
-
- args::Flag cmd_pickSession(parser, "pick-session", "Show all available sessions and select which one to open", { "pick-session" });
- args::ValueFlag<std::string> cmd_session(parser, "session", "Open the specified session.", { 's', "session" });
-
- args::PositionalList<std::string> cmd_args(parser, "URL(s)", "List of URLs to open");
- cmd_args.KickOut(true);
-
- try {
- auto next = parser.ParseArgs(args);
-
- if(cmd_version)
- return builtins::version();
- if(cmd_build)
- return builtins::build();
-
- // create and load configuration
- auto conf = init_conf(args::get(cmd_config));
- conf.ptr->make_global();
- spdlog::debug("Loading configuration {}", conf.path);
+ // argc, argv, allowSecondary
+ Browser app(argc, argv);
+ // set this, otherwise the webview becomes black when using a stylesheet
+ app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
- if(cmd_args) {
- const auto front = args::get(cmd_args).front();
- const auto cmd = commands.find(front);
- if(cmd != commands.end()) {
- return cmd->second(argv[0], next, std::end(args));
- }
- }
-
- // argc, argv, allowSecondary
- Browser app(argc, argv);
- // set this, otherwise the webview becomes black when using a stylesheet
- app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
-
- {
- // load plugins
- for(const QString &path : Util::files(conf.ptr->value<QString>("plugins.path").value(), { "*.so", "*.dll" })) {
- if(app.loadPlugin(path)) {
- spdlog::debug("Loaded plugin [{}]", qUtf8Printable(path));
- } else {
- spdlog::warn("Failed loading plugin [{}]", qUtf8Printable(path));
- }
- }
- }
-
- const auto profile = conf.ptr->value<QString>("profile.default").value();
-
- QStringList urls;
- for(const auto &u : args::get(cmd_args)) {
- urls.append(QString::fromStdString(u));
- }
- if(urls.isEmpty()) {
- urls.append(QString());
- }
+ const auto f = command_line::process<Browser>(app, subcommands, "session");
- // if app is primary, create new sessions from received messages
- if(app.isPrimary() && !cmd_noRemote) {
- QObject::connect(&app, &Browser::receivedMessage, &app, [&app](quint32 instanceId, QByteArray message) {
- Q_UNUSED(instanceId);
- JsonSession session(message);
- app.open(session.get());
- });
- }
-
- {
- const auto session = [&]() {
- if(cmd_session) {
- QFile sessionJson(QString::fromStdString(args::get(cmd_session)));
- if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) {
- return JsonSession(sessionJson.readAll());
- }
- }
- if(cmd_pickSession) {
- SessionDialog dlg;
- if(const auto pick = dlg.pickSession()) {
- return JsonSession(pick.value());
- }
- }
- return JsonSession(profile, urls);
- }();
-
- if(app.isPrimary() || cmd_noRemote) {
- app.open(session.get());
+ {
+ Configuration conf;
+ const auto plugins_path = conf.value<QString>("plugins.path").value();
+ // load plugins
+ /*for(const QString &path : Util::files(conf.value<QString>("plugins.path").value(), { "*.so", "*.dll" })) {
+ if(app.loadPlugin(path)) {
+ spdlog::debug("Loaded plugin [{}]", qUtf8Printable(path));
} else {
- // app is secondary and not standalone
- return app.sendMessage(session.serialize());
+ spdlog::warn("Failed loading plugin [{}]", qUtf8Printable(path));
}
- }
-
- return app.exec();
+ }*/
+ }
- } catch(args::Help &) {
- std::cout << parser;
- return 0;
+ return f(app);
- } catch(args::Error &e) {
- std::cerr << e.what() << std::endl;
- std::cerr << parser;
- return -1;
- }
}
diff --git a/src/meson.build b/src/meson.build
index f3ef381..1529a95 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,25 +20,25 @@ poi_icons_h = custom_target('poi_icons_h',
command: [ python3, rcc_exe, '-o=@OUTPUT@', 'dump', '-ns=icons', '@INPUT@' ])
subdir('about')
+subdir('bookmarks')
+subdir('configuration')
+subdir('cmd')
+subdir('session')
subdir('webengine')
poi_sourceset.add(mod_qt5.preprocess(
moc_headers: ['browser.h', 'applicationmenu.h',
'mainwindow/mainwindow.h', 'mainwindow/addressbar.h', 'mainwindow/menubar.h', 'mainwindow/widgets/completer.h', 'mainwindow/widgets/urllineedit.h', 'mainwindow/widgets/dockwidget.h', 'mainwindow/widgets/navigationbar.h', 'mainwindow/widgets/searchform.h',
- 'bookmarks/bookmarkswidget.h', 'bookmarks/editbookmarkdialog.h',
'session/savesessiondialog.h', 'session/sessiondialog.h',
'subwindow/subwindow.h', 'subwindow/tabwidget.h' ],
ui_files: [
'mainwindow/addressbar.ui', 'mainwindow/widgets/searchform.ui',
- 'bookmarks/bookmarksform.ui', 'bookmarks/editbookmarkdialog.ui',
'session/savesessiondialog.ui', 'session/sessiondialog.ui' ],
dependencies: dep_qt5
))
poi_sourceset.add(files(
- 'main.cpp', 'builtins.cpp',
- 'browser.cpp', 'applicationmenu.cpp',
- 'util.cpp', 'util.h',
+ 'main.cpp', 'browser.cpp', 'applicationmenu.cpp', 'util.cpp',
'mainwindow/mainwindow.cpp',
'mainwindow/addressbar.cpp',
@@ -50,8 +50,6 @@ poi_sourceset.add(files(
'mainwindow/widgets/navigationbar.cpp',
'mainwindow/widgets/searchform.cpp',
- 'bookmarks/builtins.cpp', 'bookmarks/bookmarkswidget.cpp', 'bookmarks/editbookmarkdialog.cpp', 'bookmarks/bookmarkstoolbar.cpp',
-
'session/savesessiondialog.cpp',
'session/sessiondialog.cpp',
diff --git a/src/session/builtin.cpp b/src/session/builtin.cpp
new file mode 100644
index 0000000..0359b42
--- /dev/null
+++ b/src/session/builtin.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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/cgit/smolbote
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "browser.h"
+#include "configuration.h"
+#include "session/sessiondialog.h"
+#include "session_json.hpp"
+#include <QCommandLineParser>
+#include <variant>
+
+namespace builtins
+{
+int sub_session(const QStringList &args, Browser &app)
+{
+ const auto default_profile = []() {
+ Configuration conf;
+ return conf.value<QString>("profile.default").value();
+ }();
+
+ const auto session = args.count() == 0 ? JsonSession(default_profile, args) : [&]() {
+ const QCommandLineOption profile({ "p", "profile" }, "Create session with specified profile.", "profile-id", default_profile);
+ const QCommandLineOption pick("pick", "Show all available sessions and select which one to open.");
+ const QCommandLineOption open({ "o", "open" }, "Open the specified session.", "file");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("session");
+ parser.addHelpOption();
+ parser.addOptions({ pick, open, profile });
+
+ parser.process(args);
+
+ if(parser.isSet(pick)) {
+ SessionDialog dlg;
+ if(const auto s = dlg.pickSession()) {
+ return JsonSession(s.value());
+ }
+
+ } else if(parser.isSet(open)) {
+ QFile sessionJson(parser.value(open));
+ if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return JsonSession(sessionJson.readAll());
+ }
+ }
+
+ return JsonSession(parser.isSet(profile) ? parser.value(profile) : default_profile, args);
+ }();
+
+ if(app.isPrimary() || !app.remoteEnabled()) {
+ app.open(session.get());
+ return app.exec();
+ }
+
+ app.sendMessage(session.serialize());
+ return 0;
+}
+}
diff --git a/src/session/meson.build b/src/session/meson.build
new file mode 100644
index 0000000..5ba8ffd
--- /dev/null
+++ b/src/session/meson.build
@@ -0,0 +1 @@
+poi_sourceset.add(files('builtin.cpp'))
diff --git a/subprojects/args.wrap b/subprojects/args.wrap
deleted file mode 100644
index ac19afa..0000000
--- a/subprojects/args.wrap
+++ /dev/null
@@ -1,6 +0,0 @@
-[wrap-file]
-directory = args.hxx-6.2.2
-
-source_url = https://neueland.iserlohn-fortress.net/releases/args.hxx-6.2.2.tar.xz
-source_filename = args.hxx-6.2.2.tar.xz
-source_hash = c1ed4bc76d3c343f493e6ae2c10ebcf3fdfaf013210b0a3dead04cef30c63fb6
diff --git a/test/cli/cli.cpp b/test/cli/cli.cpp
deleted file mode 100644
index 322c365..0000000
--- a/test/cli/cli.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <linenoise.h>
-#include <Python.h>
-
-static int numargs=0;
-
-/* Return the number of arguments of the application command line */
-static PyObject* emb_numargs(PyObject *self, PyObject *args)
-{
- if(!PyArg_ParseTuple(args, ":numargs"))
- return NULL;
-
- numargs = 19;
- return PyLong_FromLong(numargs);
-}
-
-static PyMethodDef EmbMethods[] = {
- /* ml_name Name of the method
- * ml_meth pointer to C implementation
- * ml_flags flag bits indicating how it should be called
- * ml_doc docstring
- */
- {"numargs", emb_numargs, METH_VARARGS, "Return the number of arguments received by the process."},
- {NULL, NULL, 0, NULL}
-};
-
-static PyModuleDef EmbModule = {
- /* m_base */ PyModuleDef_HEAD_INIT, // base module, always HEAD_INIT
- /* m_name */ "emb", // module name
- /* m_doc */ NULL, // Docstring for the module; usually a docstring variable created with PyDoc_STRVAR()
- /* m_size */ -1, //
- /* m_methods */ EmbMethods, // A pointer to a table of module-level functions
- /* m_slots */ NULL, // An array of slot definitions for multi-phase initialization
- /* traverse */ NULL, // A traversal function to call during GC traversal of the module object
- /* clear */ NULL, // A clear function to call during GC clearing of the module object
- /* free */ NULL // A function to call during deallocation of the module object
-};
-
-static PyObject* PyInit_emb(void)
-{
- return PyModule_Create(&EmbModule);
-}
-
-int main(int argc, char** argv)
-{
- printf("cli test application\n");
-
- wchar_t *program = Py_DecodeLocale(argv[0], NULL);
- if (program == NULL) {
- fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
- exit(1);
- }
-
- // inform the interpreter about paths to run-time libraries
- Py_SetProgramName(program); /* optional but recommended */
-
- printf("import emb: %i\n", PyImport_AppendInittab("emb", &PyInit_emb));
-
- // Initialize the python interpreter
- Py_Initialize();
-
- PyRun_SimpleString("print('Python interpreter ready')\n");
-
- const char* prompt = "poi> ";
-
- while(true) {
- char *cmd = linenoise(prompt);
-
- if(cmd == nullptr || *cmd == '\0') {
- printf("breaking out of repl\n");
- free(cmd);
- break;
- }
-
- //printf("echo(%i):'%s'\n", strlen(cmd), cmd);
- PyRun_SimpleString(cmd);
- linenoiseHistoryAdd(cmd);
- free(cmd);
- }
-
- // finalize the interpreter
- if (Py_FinalizeEx() < 0) {
- exit(120);
- }
-
- //
- PyMem_RawFree(program);
-
- return 0;
-}
-
diff --git a/test/cli/meson.build b/test/cli/meson.build
deleted file mode 100644
index 298e1db..0000000
--- a/test/cli/meson.build
+++ /dev/null
@@ -1,4 +0,0 @@
-cli_demo = executable('cli', install: false, dependencies: [ optional_deps ],
- sources: [ 'cli.cpp' ]
-)
-