From f3a4607d6a722a862af0eb9747a15dcdf624b6fb Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Sun, 3 Nov 2019 00:18:10 +0200 Subject: Drop boost dependency - wrote not-invented-here config file parser and conf class - spent obscene amount of time plugging in said conf class --- lib/configuration/Kconfig | 131 ----------------------- lib/configuration/configuration.cpp | 205 ++++++++++++++---------------------- lib/configuration/configuration.h | 136 ++++++++---------------- lib/configuration/meson.build | 14 +-- 4 files changed, 127 insertions(+), 359 deletions(-) delete mode 100644 lib/configuration/Kconfig (limited to 'lib/configuration') diff --git a/lib/configuration/Kconfig b/lib/configuration/Kconfig deleted file mode 100644 index b165093..0000000 --- a/lib/configuration/Kconfig +++ /dev/null @@ -1,131 +0,0 @@ -menu "Configuration defaults" - config PATH_CONFIG - string "Configuration location" - default "~/.config/smolbote/smolbote.cfg" - config PATH_FILTER - string "Host filter path" - default "~/.config/smolbote/hosts.d" - config PATH_PLUGINS - string "Plugin load location" - default "~/.config/smolbote/plugins.d" - config PATH_PROFILES - string "Profile load location" - default "~/.config/smolbote/profiles.d" - config PATH_BOOKMARKS - string "Bookmarks location" - default "~/.config/smolbote/bookmarks.xbel" - config PATH_DOWNLOADS - string "Downloads location" - default "~/Downloads" - config PATH_SESSION - string "Session location" - default "~/.config/smolbote/session.d" -endmenu - -menu "Keyboard shortcuts" - comment "Main Window shortcuts" - config SHORTCUT_WINDOW_NEWGROUP - string "New Group shortcut" - default "Ctrl+G" - config SHORTCUT_WINDOW_NEWWINDOW - string "New Window shortcut" - default "Ctrl+N" - config SHORTCUT_WINDOW_ABOUT - string "Show About Dialog" - default "Ctrl+H" - config SHORTCUT_WINDOW_QUIT - string "Quit shortcut" - default "Ctrl+Q" - - config SHORTCUT_WINDOW_SEARCH - string "Show or hide search box" - default "F3" - - config SHORTCUT_WINDOW_BOOKMARKS - string "Show bookmarks dialog in this window" - default "Ctrl+B" - config SHORTCUT_WINDOW_DOWNLOADS - string "Show downloads dialog in this window" - default "Ctrl+D" - - comment "Navigation Bar shortcuts" - config SHORTCUT_NAVIGATION_BACK - string "Go back in history" - default "Ctrl+Left" - config SHORTCUT_NAVIGATION_BACKMENU - string "Show Back history menu" - default "Ctrl+Down" - config SHORTCUT_NAVIGATION_FORWARD - string "Go forward in history" - default "Ctrl+Right" - config SHORTCUT_NAVIGATION_FORWARDMENU - string "Show Forward history menu" - default "Ctrl+Up" - config SHORTCUT_NAVIGATION_REFRESH - string "Refresh the page" - default "F5" - config SHORTCUT_NAVIGATION_RELOAD - string "Reload the page" - default "Ctrl+F5" - config SHORTCUT_NAVIGATION_HOME - string "Load homepage" - default "Ctrl+Home" - - comment "Address Bar shortcuts" - config SHORTCUT_ADDRESS_FOCUS - string "Focus the Address Bar" - default "F4" - config SHORTCUT_ADDRESS_MENU - string "Show Address Bar menu" - default "F2" - - comment "Subwindow shortcuts" - config SHORTCUT_SUBWINDOW_MENU - string "Subwindow context menu" - default "F1" - config SHORTCUT_SUBWINDOW_TILE - string "Tile subwindows" - default "F9" - config SHORTCUT_SUBWINDOW_CASCADE - string "Cascade subwindow" - default "F10" - config SHORTCUT_SUBWINDOW_FULLSCREEN - string "Make current subwindow fullscreen" - default "F11" - - config SHORTCUT_SUBWINDOW_NEWTAB - string "Create new tab" - default "Ctrl+T" - config SHORTCUT_SUBWINDOW_CLOSETAB - string "Close current tab" - default "Ctrl+X" - config SHORTCUT_SUBWINDOW_TABLEFT - string "Switch to the tab on the left" - default "Ctrl+O" - config SHORTCUT_SUBWINDOW_MOVETABLEFT - string "Move tab to the left" - default "Ctrl+Shift+O" - config SHORTCUT_SUBWINDOW_TABRIGHT - string "Switch to the tab on the right" - default "Ctrl+P" - config SHORTCUT_SUBWINDOW_MOVETABRIGHT - string "Move tab to the right" - default "Ctrl+Shift+P" - -endmenu - -menu "Profile defaults" - config PROFILE_DEFAULT - string "Default profile" - default "" - config PROFILE_DEFAULT_SEARCH - string "Search engine" - default "https://duckduckgo.com/?q=%1&ia=web" - config PROFILE_DEFAULT_HOMEPAGE - string "Homepage" - default "about:blank" - config PROFILE_DEFAULT_NEWTAB - string "New tab page" - default "about:blank" -endmenu - diff --git a/lib/configuration/configuration.cpp b/lib/configuration/configuration.cpp index 952f4b2..bd7700c 100644 --- a/lib/configuration/configuration.cpp +++ b/lib/configuration/configuration.cpp @@ -7,146 +7,97 @@ */ #include "configuration.h" -#include -#include -#include +#include +#include +#include #include -#include -#include "config.h" +#include -namespace po = boost::program_options; +static std::unique_ptr s_conf; -inline std::string defaultUserConfigLocation() +inline void strip(std::string &value) { -#ifdef CONFIG_PATH_CONFIG - return CONFIG_PATH_CONFIG; -#else - // try to locate an existing config - QString path = QStandardPaths::locate(QStandardPaths::ConfigLocation, "smolbote/smolbote.cfg"); + value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to(), ' '))); + value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to(), ' ')).base(), value.end()); +} - // it's possible there is no config, so set the path properly - if(path.isEmpty()) - path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/smolbote/smolbote.cfg"; - return path.toStdString(); -#endif +Configuration::Configuration() + : use_global(true) +{ + if(!s_conf) + throw new std::runtime_error("Trying to use default Configuration, but none has been set!"); } -Configuration::Configuration(int argc, char **argv, const std::string &path, QObject *parent) - : QObject(parent) - , m_homePath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()) +Configuration::Configuration(std::initializer_list> l) noexcept + : m_homePath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()) { - configuration_desc.add_options() - ("browser.stylesheet", po::value()) - ("browser.iconTheme", po::value(), "Icon theme") - ("browser.locale", po::value(), "Set Qt localization.") - ("browser.translation", po::value(), "Set application localization.") - - // sessions - ("browser.session.path", po::value()->default_value(CONFIG_PATH_SESSION)) - -#ifdef CONFIG_USEBREAKPAD - ("browser.crash.path", po::value()->default_value(CONFIG_PATH_CRASHDUMP)) - ("browser.crash.handler", po::value()->default_value(CONFIG_PATH_CRASHHANDLER)) -#endif - - // main window ui - ("mainwindow.height", po::value()->default_value(720)) - ("mainwindow.width", po::value()->default_value(1280)) - ("mainwindow.maximized", po::value()->default_value(true)) - ("mainwindow.title", po::value()->default_value(CONFIG_POI_NAME)) - - // main window shortcuts - ("mainwindow.shortcuts.saveSession", po::value()->default_value("Ctrl+S,S")) - ("mainwindow.shortcuts.openSession", po::value()->default_value("Ctrl+S,O")) - - ("mainwindow.shortcuts.newGroup", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_NEWGROUP)) - ("mainwindow.shortcuts.newWindow", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_NEWWINDOW)) - - ("mainwindow.shortcuts.about", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_ABOUT)) - ("mainwindow.shortcuts.quit", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_QUIT)) - - ("mainwindow.shortcuts.search", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_SEARCH)) - ("mainwindow.shortcuts.tileWindows", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_TILE)) - ("mainwindow.shortcuts.cascadeWindows", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_CASCADE)) - - // navigation - ("navigation.movable", po::value()->default_value(false)) - ("navigation.shortcuts.back", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_BACK)) - ("navigation.shortcuts.backMenu", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_BACKMENU)) - ("navigation.shortcuts.forward", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_FORWARD)) - ("navigation.shortcuts.forwardMenu", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_FORWARDMENU)) - ("navigation.shortcuts.refresh", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_REFRESH)) - ("navigation.shortcuts.reload", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_RELOAD)) - ("navigation.shortcuts.home", po::value()->default_value(CONFIG_SHORTCUT_NAVIGATION_HOME)) - - // address bar - ("addressbar.shortcuts.focus", po::value()->default_value(CONFIG_SHORTCUT_ADDRESS_FOCUS)) - ("addressbar.shortcuts.menu", po::value()->default_value(CONFIG_SHORTCUT_ADDRESS_MENU)) - - // subwindow - ("subwindow.shortcuts.menu", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_MENU)) - ("subwindow.shortcuts.new", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_NEWTAB)) - ("subwindow.shortcuts.close", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_CLOSETAB)) - ("subwindow.shortcuts.restoreTab", po::value()->default_value("Ctrl+Shift+T")) - ("subwindow.shortcuts.left", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_TABLEFT)) - ("subwindow.shortcuts.moveLeft", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_MOVETABLEFT)) - ("subwindow.shortcuts.right", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_TABRIGHT)) - ("subwindow.shortcuts.moveRight", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_MOVETABRIGHT)) - ("subwindow.shortcuts.fullscreen", po::value()->default_value(CONFIG_SHORTCUT_SUBWINDOW_FULLSCREEN)) - - // Filter settings - ("filter.hosts", po::value()->default_value(CONFIG_PATH_FILTER)) - ("filter.adblock", po::value()) - ("filter.header", po::value>()) -// ("filter.cookies.block.all", po::value()->default_value(false)) -// ("filter.cookies.block.thirdParty", po::value()->default_value(true)) -// ("filter.cookies.path", po::value()->default_value("~/.config/smolbote/cookies.d")) - - // Plugin settings - ("plugins.path", po::value()->default_value(CONFIG_PATH_PLUGINS)) - - // Profile settings - // default profile name the browser should use; "" is off-the-record - ("profile.default", po::value()->default_value(CONFIG_PROFILE_DEFAULT)) - ("profile.path", po::value()->default_value(CONFIG_PATH_PROFILES)) - ("profile.search", po::value()->default_value(CONFIG_PROFILE_DEFAULT_SEARCH)) - ("profile.homepage", po::value()->default_value(CONFIG_PROFILE_DEFAULT_HOMEPAGE)) - ("profile.newtab", po::value()->default_value(CONFIG_PROFILE_DEFAULT_NEWTAB)) - - // Bookmark settings - ("bookmarks.path", po::value()->default_value(CONFIG_PATH_BOOKMARKS)) - ("bookmarks.shortcut", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_BOOKMARKS)) - - // Downloads settings - ("downloads.path", po::value()->default_value(CONFIG_PATH_DOWNLOADS)) - ("downloads.shortcut", po::value()->default_value(CONFIG_SHORTCUT_WINDOW_DOWNLOADS)) - ; - - // po::store will only overwrite values that are default, so: - // 1. parse command line - { - auto cmd = po::command_line_parser(argc, argv); - cmd.allow_unregistered(); - cmd.options(configuration_desc); - po::store(cmd.run(), vm); - } - // 2. parse config file - { - std::ifstream f(path, std::ifstream::in); - // parse_config_file(file, options_description, allow_unregistered) - po::store(po::parse_config_file(f, configuration_desc, true), vm); - } + for(const auto &i : l) { + insert_or_assign(i.first, i.second); + } } -QHash Configuration::section(const std::string &prefix) const +void Configuration::read(std::basic_istream &input) { - QHash v; - for(auto &s : configuration_desc.options()) { - if(boost::starts_with(s->long_name(), prefix)) { - v[s->long_name().c_str()] = QString::fromStdString(value(s->long_name().c_str()).value()); + std::string line, key, value; + std::istringstream is_line; + + while(std::getline(input, line)) { + if(line[0] == '#' || line.length() == 0) + continue; + + is_line.clear(); + is_line.str(line); + + if(std::getline(is_line, key, '=')) { + is_line >> value; + + strip(key); + strip(value); + + if(this->count(key) == 0) { + // no type has been specified for this key, assuming std::string + insert(std::make_pair(key, value)); + continue; + } + + auto v = at(key); + if(std::holds_alternative(v)) { + at(key) = value; + } else if(std::holds_alternative(v)) { + at(key) = std::stoi(value); + } else if(std::holds_alternative(v)) { + at(key) = (value == "true"); + } + } } +} + - return v; +void Configuration::move_global(std::unique_ptr &&conf) +{ + s_conf = std::move(conf); +} + +Configuration* Configuration::instance() +{ + return s_conf.get(); +} + +void setShortcut(QAction *action, const char *name) +{ + if(!s_conf) + throw new std::runtime_error("Trying to set a shortcut, but no configuration has been set!"); + + if(const auto shortcutText = s_conf->value(name)) { + const QString tooltip = action->toolTip(); + action->setShortcut(QKeySequence::fromString(shortcutText.value())); + action->setToolTip(QString("%1 (%2)").arg(tooltip, shortcutText.value())); + } +#ifdef QT_DEBUG + else { + std::cout << "fixme: setShortcut called for " << name << ", but no such value exists!" << std::endl; + } +#endif } diff --git a/lib/configuration/configuration.h b/lib/configuration/configuration.h index 40da50e..bc8f52e 100644 --- a/lib/configuration/configuration.h +++ b/lib/configuration/configuration.h @@ -9,121 +9,77 @@ #ifndef SMOLBOTE_CONFIGURATION_H #define SMOLBOTE_CONFIGURATION_H -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include -class Configuration : public QObject +typedef std::variant conf_value_t; + +class [[clang::consumable(unconsumed)]] Configuration : private std::unordered_map { - Q_OBJECT public: - explicit Configuration(int argc, char** argv, const std::string &path, QObject *parent = nullptr); + [[clang::return_typestate(unconsumed)]] + explicit Configuration(); + + [[clang::return_typestate(unconsumed)]] + explicit Configuration(std::initializer_list> l) noexcept; + + explicit Configuration(Configuration &&other [[clang::param_typestate(unconsumed)]]) = default; + ~Configuration() = default; - bool exists(const char *path) - { - return vm.count(path) ? true : false; - } + [[clang::callable_when(unconsumed)]] + void read(std::basic_istream &input); template - std::optional value(const char *path) const + [[clang::callable_when(unconsumed)]] std::optional value(const char *path) const { - // if setting doesn't exist, we crash - // in debug builds, check if setting exists - - if(vm.count(path) == 0) { + if(use_global) + return instance()->value(path); + + if(this->count(path) == 0) { return std::nullopt; } - // path is guaranteed to exist, so using vm[path] is safe - - if constexpr(std::is_same_v) { - QStringList r; - for(const std::string &item : vm[path].as>()) { - r.append(QString::fromStdString(item)); - } - return std::optional(r); - - } else if constexpr(std::is_same_v || std::is_same_v) { - - if(vm[path].value().type() == typeid(int)) { - if constexpr(std::is_same_v) - return std::optional(std::to_string(vm[path].as())); - else if constexpr(std::is_same_v) - return std::optional(QString::number(vm[path].as())); - } - - if(vm[path].value().type() == typeid(bool)) { - return std::optional(vm[path].as() ? "true" : "false"); - } - - std::string r = vm[path].as(); + // path is guaranteed to exist + const auto value = at(path); - // check if it's a path - if(r.front() == '~') { + if constexpr(std::is_same_v || std::is_same_v) { + auto r = std::get(value); + if(r.front() == '~') r.replace(0, 1, m_homePath); - } - if constexpr(std::is_same_v) - return std::optional(r); - else if constexpr(std::is_same_v) - return std::optional(QString::fromStdString(r)); + if constexpr(std::is_same_v) + return std::make_optional(QString::fromStdString(r)); + else + return std::make_optional(r); - } else - return std::optional(vm[path].as()); - } - - template - void setValue(const char *path, const T &value) - { - if(vm.count(path) == 0) { - qWarning("value(%s) does not exist", path); - } - - vm.at(path).value() = value; - - emit settingChanged(path, value); - } + } else if constexpr(std::is_same_v) { + return std::make_optional(QString::fromStdString(std::get(value)).split(';')); - void setShortcut(QAction *action, const char *name) const - { - Q_CHECK_PTR(action); - - const auto shortcutText = this->value(name); - if(shortcutText) { - const QString tooltip = action->toolTip(); - action->setShortcut(QKeySequence::fromString(shortcutText.value())); - action->setToolTip(QString("%1 (%2)").arg(tooltip, shortcutText.value())); - - connect(this, &Configuration::settingChanged, action, [=](const std::string &path, const QString &value) { - if(path == name) { - action->setShortcut(QKeySequence::fromString(value)); - action->setToolTip(QString("%1 (%2)").arg(tooltip, value)); - } - }); - } - } - - QHash section(const std::string &prefix) const; - const boost::program_options::options_description& description() const - { - return configuration_desc; - } + } else if (std::holds_alternative(value)) { + return std::optional(std::get(value)); + } else + return std::nullopt; + + } // std::optional value(path) const -signals: - void settingChanged(const std::string &path, const QString &value); + static void move_global(std::unique_ptr &&conf); private: - boost::program_options::options_description configuration_desc; - boost::program_options::variables_map vm; + static Configuration *instance(); const std::string m_homePath; + const bool use_global = false; }; +void setShortcut(QAction *action, const char *name); + #endif // SMOLBOTE_CONFIGURATION_H diff --git a/lib/configuration/meson.build b/lib/configuration/meson.build index 19f57f4..939a493 100644 --- a/lib/configuration/meson.build +++ b/lib/configuration/meson.build @@ -1,13 +1,5 @@ -configuration_moc = mod_qt5.preprocess( - moc_headers: ['configuration.h'], - dependencies: dep_qt5 -) - -configuration_lib = static_library('configuration', ['configuration.cpp', configuration_moc], - dependencies: [dep_boost, dep_qt5, autogen_config] -) - dep_configuration = declare_dependency( - include_directories: include_directories('.'), - link_with: configuration_lib + include_directories: include_directories('.'), + link_with: static_library('configuration', ['configuration.cpp'], dependencies: dep_qt5) ) + -- cgit v1.2.1