diff options
69 files changed, 1016 insertions, 1212 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d83676c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.16) + +project(smolbote VERSION 0.1.0 LANGUAGES CXX) + +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + +include(CppCheck) +include(CompilerOptions) +include(FeatureSummary) +include(GNUInstallDirs) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# set C++ standard for all targets +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# set PIE for all targets +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# dependencies +find_package(GTest) +enable_testing() + +find_package(spdlog REQUIRED) + +find_package(Qt6 6.0.0 REQUIRED COMPONENTS + Network + Widgets + SvgWidgets + WebEngineCore + WebEngineWidgets + Concurrent +) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") +add_subdirectory(third_party/SingleApplication) + +add_subdirectory(lib/bookmarks) +add_subdirectory(lib/configuration) +add_subdirectory(lib/downloads) +#add_subdirectory(lib/pluginloader) +add_subdirectory(lib/session_formats) +add_subdirectory(lib/urlfilter) + +add_subdirectory(src/about) +add_subdirectory(src/webengine) +add_subdirectory(src) + +add_subdirectory(doc) + +install(TARGETS poi DESTINATION ${CMAKE_INSTALL_BINDIR}) + +feature_summary(WHAT ALL) diff --git a/CppCheckSuppressions.txt b/CppCheckSuppressions.txt new file mode 100644 index 0000000..6ed9d86 --- /dev/null +++ b/CppCheckSuppressions.txt @@ -0,0 +1,3 @@ +// for all files +missingIncludeSystem +unknownMacro diff --git a/Kconfig b/Kconfig deleted file mode 100644 index 37d3d9c..0000000 --- a/Kconfig +++ /dev/null @@ -1,226 +0,0 @@ -menu "Application" - config POI_NAME - string "Application name" - default "smolbote" - config POI_ICON - string "Path to icon" - default ":/icons/poi.svg" - config POI_CFG_PATH - string "Configuration location" - default "~/.config/smolbote/smolbote.cfg" -endmenu - -menu "Keyboard shortcuts" - comment "Main Window shortcuts" - config shortcuts.session.save - string "Save Session shortcut" - default "Ctrl+S,S" - config shortcuts.session.open - string "Load Session shortcut" - default "Ctrl+S,O" - config shortcuts.window.newgroup - string "New Group shortcut" - default "Ctrl+G" - config shortcuts.window.newwindow - string "New Window shortcut" - default "Ctrl+N" - config shortcuts.window.about - string "Show About Dialog" - default "F1" - config shortcuts.window.quit - string "Quit shortcut" - default "Ctrl+Q" - config shortcuts.window.search - string "Show or hide search box" - default "F3" - config shortcuts.window.downloads.show - string "Show downloads dialog in this window" - default "Ctrl+D" - - comment "Navigation Bar shortcuts" - config navigationbar.show - string "Toggle navigation bar" - default "Ctrl+Shift+N" - config shortcuts.navigation.back - string "Go back in history" - default "Ctrl+Left" - config shortcuts.navigation.backmenu - string "Show Back history menu" - default "Ctrl+Down" - config shortcuts.navigation.forward - string "Go forward in history" - default "Ctrl+Right" - config shortcuts.navigation.forwardmenu - string "Show Forward history menu" - default "Ctrl+Up" - config shortcuts.navigation.refresh - string "Refresh the page" - default "F5" - config shortcuts.navigation.reload - string "Reload the page" - default "Ctrl+F5" - config shortcuts.navigation.home - string "Load homepage" - default "Ctrl+Home" - - comment "Address Bar shortcuts" - config shortcuts.address.focus - string "Focus the Address Bar" - default "F4" - config shortcuts.address.menu - string "Show Address Bar menu" - default "F2" - - comment "Subwindow shortcuts" - config shortcuts.subwindow.close - string "Close Subwindow shortcut" - default "Ctrl+Shift+W" - config shortcuts.subwindow.fullscreen - string "Make current subwindow fullscreen" - default "F11" - config shortcuts.subwindow.newtab - string "Create new tab" - default "Ctrl+T" - config shortcuts.subwindow.closetab - string "Close current tab" - default "Ctrl+W" - config shortcuts.subwindow.restoretab - string "Restore last closed tab" - default "Ctrl+Shift+T" - config shortcuts.subwindow.tableft - string "Switch to the tab on the left" - default "Ctrl+O" - config shortcuts.subwindow.movetableft - string "Move tab to the left" - default "Ctrl+Shift+O" - config shortcuts.subwindow.tabright - string "Switch to the tab on the right" - default "Ctrl+P" - config shortcuts.subwindow.movetabright - string "Move tab to the right" - default "Ctrl+Shift+P" - -endmenu - -menu "Main Window" - config mainwindow.title - string "Main window title" - default "smolbote" - config mainwindow.width - int "Main window width" - default 1280 - config mainwindow.height - int "Main window height" - default 720 -endmenu - -menu "Bookmarks" - config bookmarks.path - string "Bookmarks location" - default "~/.config/smolbote/bookmarks.xbel" - config shortcuts.window.bookmarks.show - string "Show bookmarks dialog in this window" - default "Ctrl+B" - config bookmarks.toolbar.show - string "Toggle bookmarks toolbar visibility" - default "Ctrl+Shift+B" - config bookmarks.toolbar.movable - bool "Default bookmarks toolbar movable" - default n - config bookmarks.toolbar.visible - bool "Default bookmarks toolbar visibility" - default n -endmenu - -menu "Profile Settings" - config profile.path - string "Profile load location" - default "~/.config/smolbote/profiles.d" - config profile.default - string "Default profile" - default "" - config profile.search - string "Search engine" - default "https://duckduckgo.com/?q=%1&ia=web" - config profile.homepage - string "Homepage" - default "about:blank" - config profile.newtab - string "New tab page" - default "about:blank" -endmenu - -config USEPLUGINS - bool "Enable plugins" - default y - -menu "Plugin Settings" - depends on USEPLUGINS - config PLUGINS_PATH - string "Plugin load location" - default "~/.config/smolbote/plugins.d" - choice PLUGINS_SIGNATURE_CHECK - bool "Plugin Signature enforcement" - default PLUGINS_SIGNATURE_CHECKED - config PLUGINS_SIGNATURE_IGNORED - bool "Don't check plugin signatures" - config PLUGINS_SIGNATURE_CHECKED - bool "Don't load plugins with invalid signatures" - config PLUGINS_SIGNATURE_ENFORCED - bool "Only load plugins with valid signatures" - endchoice - config PLUGINS_SIGNATURE_HASH - string "Hashing algorithm used by the signature" - default "SHA256" -endmenu - -comment "Default paths" -config filter.path - string "Host filter path" - default "~/.config/smolbote/hosts.d" -config downloads.path - string "Downloads location" - default "~/Downloads" -config session.path - string "Session location" - default "~/.config/smolbote/session.d" - -config USEPLASMA - bool "Enable KDE Frameworks integration" - default n - -menu "KDE Integration" - depends on USEPLASMA - - config PLASMA_BLUR - bool "Enable translucent background and blur behind window" - default n - - config WALLET_FOLDER - string "KDE Wallet folder name" - default "smolbote" -endmenu - -config USEBREAKPAD - bool "Enable Breakpad integration" - default n - -menu "Breakpad Integration" - depends on USEBREAKPAD - - config PATH_CRASHDUMP - string "Crash dump location" - default "~/.config/smolbote/crash.d" - - config PATH_CRASHHANDLER - string "Crash handler location" - default "poi-crash" -endmenu - -menu "Workarounds" - config QTBUG_65223 - bool "Manually emit loadFinished" - default y - help - See QTBUG-65223: loadStarted is emitted twice when loading link with anchor -endmenu diff --git a/cmake/CompilerOptions.cmake b/cmake/CompilerOptions.cmake new file mode 100644 index 0000000..dbb37fd --- /dev/null +++ b/cmake/CompilerOptions.cmake @@ -0,0 +1,89 @@ +# ================================================================================== +# adapted from +# https://github.com/zhivkopetrov/cmake_helpers.git +# MIT License Copyright (c) 2020-2023 Zhivko Petrov +# +# some compiler warnings from +# https://lefticus.gitbooks.io/cpp-best-practices/content/02-Use_the_Tools_Available.html +# +# ================================================================================== +# target_enable_warnings + +function(target_enable_warnings target) + if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) + target_compile_options( + ${target} + PRIVATE + /W4 + /WX + ) + endif() + + if((CMAKE_CXX_COMPILER_ID MATCHES GNU) OR + (CMAKE_CXX_COMPILER_ID MATCHES Clang)) + target_compile_options( + ${target} + PRIVATE + # compiler warnings + # generic + -Wall -Wextra # reasonable and standard + -Wpedantic # non-standard cpp is used + -Wmisleading-indentation + -Wlogical-op # logical op being used instead of bitwise + -Wundef + -Wuninitialized + -Weffc++ # disabled because it would require skeleton changes + # variables + -Wshadow # variable declaration shadows one from parent context + -Wunused # anything being unused + # casts + -Werror=old-style-cast # C-style casts + -Wcast-align # performance problem casts + -Wcast-qual + -Wuseless-cast # cast to same type + # conversion + #-Wconversion # type conversion may lose data + -Wsign-conversion # sign conversion + -Wdouble-promotion # implicit float to double + # functions + -Wunused-parameter + -Werror=missing-declarations # missing function declaration in header files + -Werror=redundant-decls + -Werror=return-type + -Werror=overloaded-virtual + # inheritance + -Wnon-virtual-dtor # class with virtual functions has non-virt dtor + -Woverloaded-virtual # overloaded (not override) virtual function + # branches + -Wduplicated-cond # if/else chain has duplicated conditions + -Wduplicated-branches # if/else chain has duplicated code + # pointers + -Wpointer-arith + -Wnull-dereference + # style + -Wformat=2 # security issues around printf + -Werror=date-time # usage of __DATE__ and __TIME__ macros + -Werror=missing-field-initializers + # logic + -Wlogical-op + -Wimplicit-fallthrough + -Wduplicated-cond + -Wduplicated-branches + + # other flags + -ffunction-sections # place each function into its own section + # better ASLR but larger executables + -fstack-protector-all # emit check for buffer overflows on all functions + -fstack-clash-protection # emit check for stack clash attacks + ) + endif() + + # add -Werror to release builds + if((${CMAKE_BUILD_TYPE} MATCHES Release) OR + (${CMAKE_BUILD_TYPE} MATCHES MinSizeRel)) + # currently disabled + #target_compile_options(${target} PRIVATE -Werror) + endif() + +endfunction() + diff --git a/cmake/CppCheck.cmake b/cmake/CppCheck.cmake new file mode 100644 index 0000000..d8a4559 --- /dev/null +++ b/cmake/CppCheck.cmake @@ -0,0 +1,11 @@ +find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) + +if(CMAKE_CXX_CPPCHECK) + list(APPEND CMAKE_CXX_CPPCHECK + --std=c++20 + --enable=all + --inconclusive + --quiet + --suppressions-list=${PROJECT_SOURCE_DIR}/CppCheckSuppressions.txt + ) +endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..0f33bcc --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,38 @@ +option(INSTALL_MANPAGES "Install manpages") + +add_custom_command( + OUTPUT smolbote.1 + COMMAND ${CMAKE_CURRENT_LIST_DIR}/man/genroff.sh ${CMAKE_CURRENT_LIST_DIR}/man/smolbote.1.scd smolbote.1 + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/man/smolbote.1.scd + USES_TERMINAL +) + +add_custom_command( + OUTPUT smolboterc.5 + COMMAND ${CMAKE_CURRENT_LIST_DIR}/man/genroff.sh ${CMAKE_CURRENT_LIST_DIR}/man/smolboterc.5.scd smolboterc.5 + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/man/smolboterc.5.scd + USES_TERMINAL +) + +add_custom_command( + OUTPUT smolbote-profile.5 + COMMAND ${CMAKE_CURRENT_LIST_DIR}/man/genroff.sh ${CMAKE_CURRENT_LIST_DIR}/man/smolbote-profile.5.scd smolbote-profile.5 + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/man/smolbote-profile.5.scd + USES_TERMINAL +) + +add_custom_target(manpages DEPENDS + smolbote.1 + smolboterc.5 + smolbote-profile.5 +) + +if(${INSTALL_MANPAGES}) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/smolbote.1 + ${CMAKE_CURRENT_BINARY_DIR}/smolboterc.5 + ${CMAKE_CURRENT_BINARY_DIR}/smolbote-profile.5 + TYPE MAN +) +endif() diff --git a/doc/building.md b/doc/building.md index 9ea9344..a7feab1 100644 --- a/doc/building.md +++ b/doc/building.md @@ -1,11 +1,12 @@ ## Building from source ### Dependencies -- [Qt](https://www.qt.io/) -- [meson](https://mesonbuild.com/) 0.52.0 or later +- [Qt](https://www.qt.io/) 6 +- [cmake](https://cmake.org/) 3.16 or later - [kconfiglib](https://github.com/ulfalizer/Kconfiglib/) - A compiler with C++17 support -- spdlog: if not found can be downloaded by meson +- spdlog +- SingleApplication ### Optional dependencies - openssl or libressl: for signing plugins @@ -13,45 +14,22 @@ - gtest ### Steps -``` +```sh # clone the repository -git clone https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote.git smolbote.git -cd smolbote.git +git clone https://neueland.iserlohn-fortress.net/cgit/smolbote.git +cd smolbote # Get the submodules git submodule init git submodule update # configure -mkdir build -meson build +cmake -S . -B cmake-build # make -ninja -``` - -## Using meson - -### Listing build options -``` -build% meson configure -``` - -### Changing build options -``` -build% meson configure -D<option-name>=<enabled|disabled> -``` - -### Prevent meson from downloading wraps -During configure, meson can download missing dependencies on its own using -wraps. To disable this, pass '--wrap-mode=nodownload' during the configure -phase: -``` -repo% meson --wrap-mode=nodownload build-path +cmake --build cmake-build ``` -For more information on how to use meson, see the meson [quick quide](https://mesonbuild.com/Quick-guide.html). - ## Configuring builds smolbote uses Kconfig to store and customize features and default settings such as keyboard shortcuts and paths. You can edit these settings using `menuconfig` diff --git a/doc/man/genroff.sh b/doc/man/genroff.sh index 1d513f4..d1c74a1 100755 --- a/doc/man/genroff.sh +++ b/doc/man/genroff.sh @@ -1,4 +1,4 @@ #/usr/bin/env sh -scdoc < $1 +scdoc < $1 > $2 diff --git a/doc/meson.build b/doc/meson.build deleted file mode 100644 index 6490969..0000000 --- a/doc/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -if not get_option('manpage') - subdir_done() -endif - -sh = find_program('sh', required: true, native: true, disabler: true) - -foreach f : [ 'smolbote.1', 'smolboterc.5', 'smolbote-profile.5' ] -manpage = custom_target(f, - input: 'man'/f+'.scd', - output: '@BASENAME@', - capture: true, - command: [ sh, meson.current_source_dir()/'man/genroff.sh', '@INPUT@' ], - install: true, - install_dir: get_option('mandir') -) -endforeach diff --git a/lib/bookmarks/CMakeLists.txt b/lib/bookmarks/CMakeLists.txt new file mode 100644 index 0000000..d4bd3c7 --- /dev/null +++ b/lib/bookmarks/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(bookmarks STATIC + bookmarkformat.cpp bookmarkformat.h + bookmarkitem.cpp bookmarkitem.h + bookmarkmodel.cpp bookmarkmodel.h + formats/xbel.cpp formats/xbel.h + formats/ffjson.cpp formats/ffjson.h +) +target_link_libraries(bookmarks PUBLIC Qt6::Widgets) +target_include_directories(bookmarks PUBLIC ${CMAKE_CURRENT_LIST_DIR}) diff --git a/lib/bookmarks/formats/ffjson.cpp b/lib/bookmarks/formats/ffjson.cpp index f9e0866..f95d0d8 100644 --- a/lib/bookmarks/formats/ffjson.cpp +++ b/lib/bookmarks/formats/ffjson.cpp @@ -8,11 +8,12 @@ #include "ffjson.h" #include "bookmarkitem.h" +#include <QDateTime> +#include <QDebug> +#include <QIODevice> +#include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> -#include <QJsonArray> -#include <QDebug> -#include <QDateTime> inline auto asDate(const QJsonValue &v) { diff --git a/lib/bookmarks/formats/xbel.cpp b/lib/bookmarks/formats/xbel.cpp index 7ff79a9..a0a553f 100644 --- a/lib/bookmarks/formats/xbel.cpp +++ b/lib/bookmarks/formats/xbel.cpp @@ -15,28 +15,28 @@ inline void readChildElements(QXmlStreamReader &reader, BookmarkItem *parent) { while(reader.readNextStartElement()) { - if(reader.name() == "title") { + if(reader.name() == QLatin1String("title")) { parent->setData(BookmarkItem::Title, reader.readElementText()); - } else if(reader.name() == "dateAdded") { + } else if(reader.name() == QLatin1String("dateAdded")) { parent->setData(BookmarkItem::DateAdded, QDateTime::fromString(reader.readElementText(), Qt::RFC2822Date)); - } else if(reader.name() == "lastModified") { + } else if(reader.name() == QLatin1String("lastModified")) { parent->setData(BookmarkItem::LastModified, QDateTime::fromString(reader.readElementText(), Qt::RFC2822Date)); - } else if(reader.name() == "tags") { + } else if(reader.name() == QLatin1String("tags")) { parent->setData(BookmarkItem::Tags, reader.readElementText().split(";")); - } else if(reader.name() == "description") { + } else if(reader.name() == QLatin1String("description")) { parent->setData(BookmarkItem::Description, reader.readElementText()); - } else if(reader.name() == "folder") { + } else if(reader.name() == QLatin1String("folder")) { auto *item = new BookmarkItem({}, BookmarkItem::Folder, parent); item->setExpanded(!(reader.attributes().value("folded") == QLatin1String("yes"))); parent->appendChild(item); readChildElements(reader, item); - } else if(reader.name() == "bookmark") { + } else if(reader.name() == QLatin1String("bookmark")) { auto *item = new BookmarkItem({}, BookmarkItem::Bookmark, parent); item->setData(BookmarkItem::Href, reader.attributes().value("href").toString()); parent->appendChild(item); @@ -53,7 +53,8 @@ void Xbel::read(QIODevice *device, BookmarkItem *item) QXmlStreamReader qXmlStreamReader(device); if(qXmlStreamReader.readNextStartElement()) { - if(!(qXmlStreamReader.name() == "xbel" && qXmlStreamReader.attributes().value("version") == "1.0")) { + if(!(qXmlStreamReader.name() == QLatin1String("xbel") + && qXmlStreamReader.attributes().value("version") == QLatin1String("1.0"))) { return; } diff --git a/lib/bookmarks/meson.build b/lib/bookmarks/meson.build deleted file mode 100644 index 81c1ece..0000000 --- a/lib/bookmarks/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -bookmarks_moc = mod_qt5.preprocess( - moc_headers: [ 'bookmarkmodel.h' ], - dependencies: dep_qt5 -) - -bookmarks_lib = static_library('bookmarks', - [ bookmarks_moc, - 'bookmarkformat.cpp', 'formats/xbel.cpp', 'formats/ffjson.cpp', - 'bookmarkitem.cpp', 'bookmarkmodel.cpp' ], - dependencies: dep_qt5 -) - -dep_bookmarks = declare_dependency( - include_directories: include_directories('.'), - link_with: bookmarks_lib -) diff --git a/lib/configuration/CMakeLists.txt b/lib/configuration/CMakeLists.txt new file mode 100644 index 0000000..2e77782 --- /dev/null +++ b/lib/configuration/CMakeLists.txt @@ -0,0 +1,24 @@ +# generate init_global.cpp +add_custom_command( + OUTPUT init_global.cpp + COMMAND ${CMAKE_CURRENT_LIST_DIR}/gen-default-cfg.py + --config ${PROJECT_SOURCE_DIR}/linux/config.yaml + --template ${CMAKE_CURRENT_LIST_DIR}/init_global.cpp.in + --output init_global.cpp + DEPENDS + ${CMAKE_CURRENT_LIST_DIR}/gen-default-cfg.py + ${PROJECT_SOURCE_DIR}/linux/config.yaml +) +add_library(configuration STATIC + configuration.cpp configuration.h + init_global.cpp + qt_specialization.cpp qt_specialization.h +) +target_link_libraries(configuration PUBLIC Qt6::Gui spdlog::spdlog) +target_include_directories(configuration PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(test_configuration test/configuration.cpp) +target_link_libraries(test_configuration PUBLIC configuration GTest::gtest_main) +gtest_add_tests(TARGET test_configuration + SOURCES test/configuration.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/test) diff --git a/lib/configuration/configuration.cpp b/lib/configuration/configuration.cpp index 75f863c..f2a2218 100644 --- a/lib/configuration/configuration.cpp +++ b/lib/configuration/configuration.cpp @@ -10,13 +10,15 @@ #include <algorithm> #include <fstream> #include <iostream> -#include <sstream> #include <stdexcept> +#include <variant> #ifndef NO_QT_SPEC #include <QStandardPaths> #endif +using namespace std::placeholders; + static std::unique_ptr<Configuration> s_conf; Configuration::Configuration() @@ -27,66 +29,64 @@ Configuration::Configuration() } } -Configuration::Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> l) noexcept +Configuration::Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> p_list) noexcept #ifndef NO_QT_SPEC : m_homePath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()) #endif { - for(const auto &i : l) { - insert_or_assign(i.first, i.second); + for(const auto &item : p_list) { + insert_or_assign(item); } } -void Configuration::read_file(const std::string &location) +bool Configuration::read_file(const std::string &location) { - std::fstream fs(location, std::fstream::in); - if(fs.is_open()) { - read(fs); - fs.close(); + std::fstream fstream(location, std::fstream::in); + bool result = false; + + if(fstream.is_open()) { + read(fstream); + fstream.close(); + result = true; } + + return result; } inline auto strip(std::string &value) { - value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to<char>(), ' '))); - value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to<char>(), ' ')).base(), value.end()); + // strip whitespace at the begining + value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind(std::not_equal_to<>(), ' ', _1))); + + // strip whitespace from the end + value.erase(std::find_if(value.rbegin(), value.rend(), std::bind(std::not_equal_to<>(), ' ', _1)).base(), value.end()); + return value; } -inline auto parse(const std::string &line, std::string §ion) +inline std::optional<Configuration::kv_pair> parse_line(const std::string &line, /* in/out */ std::string §ion) { - struct { - bool pair = false; - std::string key; - std::string value; - } ret; - - if(line[0] == '#' || line.length() == 0) { - return ret; - } - if(line.front() == '[' && line.back() == ']') { section = line.substr(1, line.length() - 2) + '/'; - return ret; + return std::nullopt; } const auto pos = line.find_first_of('='); if(pos == std::string::npos) { - return ret; + return std::nullopt; } - ret.key = line.substr(0, pos); - strip(ret.key); - if(ret.key.empty()) { - return ret; - } - ret.key = section + ret.key; + std::string key = line.substr(0, pos); + std::string val = line.substr(pos + 1); + strip(key); + strip(val); - ret.value = line.substr(pos + 1); - strip(ret.value); + if(key.empty()) { + return std::nullopt; + } + key = section + key; - ret.pair = true; - return ret; + return std::make_optional<Configuration::kv_pair>({ key, val }); } #ifdef FUZZER @@ -100,9 +100,16 @@ extern "C" int LLVMFuzzerTestOneInput(const char *Data, long long Size) void Configuration::read(std::basic_istream<char> &input) { - std::string line, section; + std::string line; + std::string section; while(std::getline(input, line)) { + // skip empty lines and comments + if(line.length() == 0 || line[0] == '#') { + continue; + } + + // include file if(line.rfind("@@") == 0) { if(line.rfind("@@include ") == 0) { read_file(line.substr(10)); @@ -110,23 +117,30 @@ void Configuration::read(std::basic_istream<char> &input) continue; } - const auto pair = parse(line, section); - if(pair.pair) { + const auto pair = parse_line(line, section); + if(pair) { + insert_or_assign(*pair); + } + } +} - if(this->count(pair.key) == 0) { - // no type has been specified for this key, assuming std::string - insert(std::make_pair(pair.key, pair.value)); - continue; - } +void Configuration::insert_or_assign(const Configuration::kv_pair &pair) +{ + // insert + if(count(pair.first) == 0) { + insert(pair); + } else { + // when assigning we have to keep the same type + const auto old_value = at(pair.first); - auto v = at(pair.key); - if(std::holds_alternative<std::string>(v)) { - at(pair.key) = pair.value; - } else if(std::holds_alternative<int>(v)) { - at(pair.key) = std::stoi(pair.value); - } else if(std::holds_alternative<bool>(v)) { - at(pair.key) = (pair.value == "true"); - } + if(std::holds_alternative<std::string>(old_value)) { + at(pair.first) = pair.second; + + } else if(std::holds_alternative<int>(old_value)) { + at(pair.first) = std::stoi(std::get<std::string>(pair.second)); + + } else if(std::holds_alternative<bool>(old_value)) { + at(pair.first) = (std::get<std::string>(pair.second) == "true"); } } } @@ -150,9 +164,7 @@ std::ostream &operator<<(std::ostream &out, const Configuration &obj) // unordered_map is, well, unordered, so grab the keys and sort them before printing them std::vector<std::string> keys; - for(const auto &pair : obj) { - keys.emplace_back(pair.first); - } + std::transform(obj.cbegin(), obj.cend(), std::inserter(keys, keys.end()), [](const auto &pair) { return pair.first; }); std::sort(keys.begin(), keys.end()); for(const auto &key : keys) { diff --git a/lib/configuration/configuration.h b/lib/configuration/configuration.h index cd3c244..f2de92f 100644 --- a/lib/configuration/configuration.h +++ b/lib/configuration/configuration.h @@ -12,52 +12,44 @@ #include <initializer_list> #include <memory> #include <optional> +#include <source_location> +#include <spdlog/spdlog.h> #include <string> #include <type_traits> #include <unordered_map> #include <variant> -#if defined(__clang__) -#define consumable(X) [[clang::consumable(X)]] -#define return_typestate(X) [[clang::return_typestate(X)]] -#define callable_when(X) [[clang::callable_when(X)]] -#define param_typestate(X) [[clang::param_typestate(X)]] -#else -#define consumable(X) -#define return_typestate(X) -#define callable_when(X) -#define param_typestate(X) -#endif - -typedef std::variant<std::string, int, bool> conf_value_t; +using conf_value_t = std::variant<std::string, int, bool>; template <typename T> -concept concept_value_t = std::is_arithmetic<T>::value || std::is_same<T, bool>::value || std::is_constructible<T, std::string>::value; +concept concept_value_t = std::is_arithmetic_v<T> || std::is_same_v<T, bool> || std::is_constructible_v<T, std::string>; -class consumable(unconsumed) Configuration : private std::unordered_map<std::string, conf_value_t> +class Configuration : private std::unordered_map<std::string, conf_value_t> { friend std::ostream &operator<<(std::ostream &out, const Configuration &obj); public: - return_typestate(unconsumed) Configuration(); - return_typestate(unconsumed) Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> l) noexcept; + using kv_pair = std::pair<std::string, conf_value_t>; + + Configuration(); + Configuration(std::initializer_list<std::pair<std::string, conf_value_t>> p_list) noexcept; Configuration(const Configuration &) = delete; Configuration &operator=(const Configuration &) = delete; - return_typestate(unconsumed) Configuration(Configuration && other param_typestate(unconsumed)) = default; + Configuration(Configuration &&other) = default; Configuration &operator=(Configuration &&) = delete; ~Configuration() = default; - callable_when(unconsumed) void read_file(const std::string &location); - callable_when(unconsumed) void read(std::basic_istream<char> & input); + bool read_file(const std::string &location); + void read(std::basic_istream<char> &input); template <typename T> - callable_when(unconsumed) [[nodiscard]] std::optional<T> value(const char *path) const + [[nodiscard]] std::optional<T> value(const char *path, const std::source_location location = std::source_location::current()) const { if(use_global) { - return instance()->value<T>(path); + return instance()->value<T>(path, location); } if(count(path) == 0) { @@ -68,18 +60,19 @@ public: } template <concept_value_t T> - callable_when(unconsumed) [[nodiscard]] std::optional<T> value(const char *path) const + [[nodiscard]] std::optional<T> value(const char *p_path, const std::source_location location = std::source_location::current()) const { if(use_global) { - return instance()->value<T>(path); + return instance()->value<T>(p_path, location); } - if(this->count(path) == 0) { + if(this->count(p_path) == 0) { + spdlog::warn("requested non-existent configuration value {} at {}:{}", p_path, location.file_name(), location.line()); return std::nullopt; } // path is guaranteed to exist - const auto value = at(path); + const auto value = at(p_path); if(std::holds_alternative<int>(value)) { if constexpr(std::is_arithmetic<T>::value) { @@ -89,9 +82,11 @@ public: } } else if(std::holds_alternative<bool>(value)) { - if constexpr(std::is_constructible<T, bool>::value) { + if constexpr(std::is_constructible_v<T, bool> || std::is_same_v<T, bool>) { return std::get<bool>(value); - } else if constexpr(std::is_constructible<T, const char *>::value) { + } else if constexpr(std::is_arithmetic_v<T>) { + return static_cast<T>(std::get<bool>(value)); + } else if constexpr(std::is_constructible_v<T, const char *>) { return std::get<bool>(value) ? T{ "true" } : T{ "false" }; } @@ -99,7 +94,9 @@ public: auto str = std::get<std::string>(value); try { - if constexpr(std::is_floating_point<T>::value) { + if constexpr(std::is_same_v<T, bool>) { + return (str == "true"); + } else if constexpr(std::is_floating_point<T>::value) { return static_cast<T>(std::stod(str)); } else if constexpr(std::is_arithmetic<T>::value) { return static_cast<T>(std::stol(str)); @@ -121,13 +118,17 @@ public: } template <typename T> - callable_when(unconsumed) T &shortcut(T & /* unused */, const char * /* unused */) const + T &shortcut(T & /* unused */, const char * /* unused */, const std::source_location location = std::source_location::current()) const { return T{}; } + static std::string init_global(const std::string &p_path); static void move_global(std::unique_ptr<Configuration> && conf); +protected: + void insert_or_assign(const kv_pair &pair); + private: static Configuration *instance(); diff --git a/lib/configuration/gen-default-cfg.py b/lib/configuration/gen-default-cfg.py new file mode 100755 index 0000000..5d9f8da --- /dev/null +++ b/lib/configuration/gen-default-cfg.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import sys +import argparse +import yaml + +def v_to_str(value): + if type(value) is str: + return f"std::string(\"{value}\")" + if type(value) is int: + return value + if type(value) is bool: + return "true" if True else "false" + +def kv_pairs_to_str(section, pairs): + pairs_str = "\n".join([f" {{ \"{section}/{key}\", {v_to_str(pairs[key])} }}," for key in pairs]) + return pairs_str + +def writeConfigInitList(config, file): + for node in config: + for section, pairs in node.items(): + print(kv_pairs_to_str(section, pairs), file=file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--config", type=argparse.FileType('r'), required=True, help="configuration yaml") + parser.add_argument("--template", type=argparse.FileType('r'), required=True, help="settings.h.in") + parser.add_argument("--output", type=argparse.FileType('w'), default=sys.stdout, help="Output location") + args = parser.parse_args() + + config = yaml.safe_load(args.config) + print("config: ", config) + + print("/* Autogenerated file", file=args.output) + print(" * config: {}".format(args.config), file=args.output) + print(" */", file=args.output) + + for line in args.template: + if "@__DEFAULT_CFG__" in line: + writeConfigInitList(config, args.output) + else: + print(line, end='', file=args.output) diff --git a/lib/configuration/init_global.cpp.in b/lib/configuration/init_global.cpp.in new file mode 100644 index 0000000..9179130 --- /dev/null +++ b/lib/configuration/init_global.cpp.in @@ -0,0 +1,25 @@ +/* + * This is a generated file. + * + * 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.git + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "configuration.h" + +std::string Configuration::init_global(const std::string &p_path) +{ + auto value_map = std::make_unique<Configuration, std::initializer_list<std::pair<std::string, conf_value_t>>>({ + /* @__DEFAULT_CFG__ */ + }); + + // load the actual configuration file + const auto cfgpath = p_path.empty() ? value_map->value<std::string>("poi/config").value() : p_path; + value_map->read_file(cfgpath); + + move_global(std::move(value_map)); + return cfgpath; +} diff --git a/lib/configuration/qt_specialization.cpp b/lib/configuration/qt_specialization.cpp index 8487e62..cd7a99a 100644 --- a/lib/configuration/qt_specialization.cpp +++ b/lib/configuration/qt_specialization.cpp @@ -1,36 +1,39 @@ #include "configuration.h" -#include <stdexcept> template <> -callable_when(unconsumed) [[nodiscard]] std::optional<QString> Configuration::value(const char *path) const +[[nodiscard]] std::optional<QString> Configuration::value(const char *path, std::source_location location) const { - const auto v = value<std::string>(path); - return v ? std::make_optional(QString::fromStdString(v.value())) : std::nullopt; + const auto result = value<std::string>(path, location); + return result ? std::make_optional(QString::fromStdString(result.value())) : std::nullopt; } template <> -callable_when(unconsumed) [[nodiscard]] std::optional<QStringList> Configuration::value(const char *path) const +[[nodiscard]] std::optional<QStringList> Configuration::value(const char *path, std::source_location location) const { - const auto v = value<std::string>(path); - return v ? std::make_optional(QString::fromStdString(v.value()).split(';')) : std::nullopt; + const auto result = value<std::string>(path, location); + return result ? std::make_optional(QString::fromStdString(result.value()).split(';')) : std::nullopt; } template <> -callable_when(unconsumed) QAction &Configuration::shortcut(QAction &action, const char *name) const +QAction &Configuration::shortcut(QAction &action, const char *name, const std::source_location location) const { - if(const auto shortcut = value<QString>(name)) { + if(const auto result = value<QString>(name, location)) { const QString old_tooltip = action.toolTip(); - action.setShortcut(QKeySequence::fromString(shortcut.value())); - action.setToolTip(QString("%1 (%2)").arg(old_tooltip, shortcut.value())); + const auto &result_value = result.value(); + + action.setShortcut(QKeySequence::fromString(result_value)); + action.setToolTip(QString("%1 (%2)").arg(old_tooltip, result_value)); } return action; } template <> -callable_when(unconsumed) QKeySequence &Configuration::shortcut(QKeySequence &sequence, const char *name) const +QKeySequence &Configuration::shortcut(QKeySequence &sequence, const char *name, const std::source_location location) const { - if(const auto shortcut = value<QString>(name)) { - sequence = QKeySequence::fromString(shortcut.value()); + if(const auto result = value<QString>(name, location)) { + const auto &result_value = result.value(); + + sequence = QKeySequence::fromString(result_value); } return sequence; } diff --git a/lib/configuration/qt_specialization.h b/lib/configuration/qt_specialization.h index 9634261..26f9593 100644 --- a/lib/configuration/qt_specialization.h +++ b/lib/configuration/qt_specialization.h @@ -5,11 +5,11 @@ #include <QStringList> template <> -callable_when(unconsumed) [[nodiscard]] std::optional<QString> Configuration::value(const char *path) const; +[[nodiscard]] std::optional<QString> Configuration::value(const char *path, std::source_location location) const; template <> -callable_when(unconsumed) [[nodiscard]] std::optional<QStringList> Configuration::value(const char *path) const; +[[nodiscard]] std::optional<QStringList> Configuration::value(const char *path, std::source_location location) const; template <> -callable_when(unconsumed) QAction &Configuration::shortcut(QAction &, const char *) const; +QAction &Configuration::shortcut(QAction &, const char *, const std::source_location) const; template <> -callable_when(unconsumed) QKeySequence &Configuration::shortcut(QKeySequence &, const char *) const; +QKeySequence &Configuration::shortcut(QKeySequence &, const char *, const std::source_location) const; diff --git a/lib/configuration/test/configuration.cpp b/lib/configuration/test/configuration.cpp new file mode 100644 index 0000000..0796c11 --- /dev/null +++ b/lib/configuration/test/configuration.cpp @@ -0,0 +1,57 @@ +#include "configuration.h" +#include <gtest/gtest.h> + +TEST(Configuration, ctor_empty_initializer_list) +{ + Configuration conf({}); + + std::cout << conf; +} + +TEST(Configuration, ctor_initializer_list) +{ + Configuration conf({ + { "some_string", "value1" }, + { "some_bool", true }, + { "some_int", 42 }, + }); + + // operator<< should not throw exceptions + std::cout << conf; + + EXPECT_EQ(*conf.value<std::string>("some_string"), "value1"); + EXPECT_EQ(*conf.value<bool>("some_bool"), true); + EXPECT_EQ(*conf.value<int>("some_int"), 42); + + EXPECT_FALSE(conf.value<int>("no_int")); +} + +TEST(Configuration, read_file) +{ + // because the init_list is empty, all types are strings + Configuration conf({}); + + EXPECT_TRUE(conf.read_file("defaultrc.ini")); + + std::cout << "---------\n"; + std::cout << conf; + std::cout << "---------\n"; + + // defaultrc.ini contents + EXPECT_EQ(*conf.value<std::string>("name"), "Top level"); + EXPECT_FALSE(conf.value<std::string>("comment")); + + EXPECT_EQ(*conf.value<std::string>("main/name"), "Section Testing"); + + // casting a string to an integer + EXPECT_EQ(*conf.value<int>("number"), 12); + + // casting a string to a boolean + EXPECT_EQ(*conf.value<std::string>("toggle"), "true"); + EXPECT_EQ(*conf.value<bool>("toggle"), true); + EXPECT_EQ(*conf.value<bool>("main/toggle"), false); + + // extrarc.ini contents + EXPECT_EQ(*conf.value<std::string>("over"), "extra"); + EXPECT_EQ(*conf.value<std::string>("extra/name"), "extra section"); +} diff --git a/lib/downloads/CMakeLists.txt b/lib/downloads/CMakeLists.txt new file mode 100644 index 0000000..b310e3a --- /dev/null +++ b/lib/downloads/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(downloads STATIC + downloadsform.ui + downloadswidget.cpp downloadswidget.h + widgets/downloaditemform.ui widgets/downloaditemwidget.cpp widgets/downloaditemwidget.h + widgets/elidedlabel.cpp widgets/elidedlabel.h +) +target_link_libraries(downloads PUBLIC Qt6::Widgets Qt6::WebEngineWidgets) +target_include_directories(downloads PUBLIC ${CMAKE_CURRENT_LIST_DIR}) diff --git a/lib/downloads/downloadswidget.cpp b/lib/downloads/downloadswidget.cpp index 02ed08a..2f192e9 100644 --- a/lib/downloads/downloadswidget.cpp +++ b/lib/downloads/downloadswidget.cpp @@ -28,7 +28,7 @@ DownloadsWidget::~DownloadsWidget() delete ui; } -void DownloadsWidget::addDownload(QWebEngineDownloadItem *item) +void DownloadsWidget::addDownload(QWebEngineDownloadRequest *item) { const QString filepath = QFileDialog::getSaveFileName(this, tr("Save File"), m_downloadPath + "/" + item->downloadFileName()); diff --git a/lib/downloads/downloadswidget.h b/lib/downloads/downloadswidget.h index eb4ce57..d0aa2b4 100644 --- a/lib/downloads/downloadswidget.h +++ b/lib/downloads/downloadswidget.h @@ -10,7 +10,7 @@ #define SMOLBOTE_DOWNLOADDIALOG_H #include <QDialog> -#include <QWebEngineDownloadItem> +#include <QWebEngineDownloadRequest> namespace Ui { @@ -26,7 +26,7 @@ public: ~DownloadsWidget() override; public slots: - void addDownload(QWebEngineDownloadItem *item); + void addDownload(QWebEngineDownloadRequest *item); private: Ui::DownloadDialog *ui; diff --git a/lib/downloads/meson.build b/lib/downloads/meson.build deleted file mode 100644 index 9b86391..0000000 --- a/lib/downloads/meson.build +++ /dev/null @@ -1,19 +0,0 @@ -downloads_inc = include_directories('.') -downloads_moc = mod_qt5.preprocess( - moc_headers: ['downloadswidget.h', 'widgets/downloaditemwidget.h', 'widgets/elidedlabel.h'], - ui_files: ['downloadsform.ui', 'widgets/downloaditemform.ui'], - dependencies: dep_qt5 -) - -downloads_lib = static_library('downloads', - ['downloadswidget.cpp', downloads_moc, - 'widgets/downloaditemwidget.cpp', 'widgets/elidedlabel.cpp'], - dependencies: dep_qt5 -) - -dep_downloads = declare_dependency( - include_directories: downloads_inc, - link_with: downloads_lib, - sources: ['downloadswidget.cpp', downloads_moc, - 'widgets/downloaditemwidget.cpp', 'widgets/elidedlabel.cpp'] -) diff --git a/lib/downloads/widgets/downloaditemwidget.cpp b/lib/downloads/widgets/downloaditemwidget.cpp index e0c8a60..169ba6f 100644 --- a/lib/downloads/widgets/downloaditemwidget.cpp +++ b/lib/downloads/widgets/downloaditemwidget.cpp @@ -30,7 +30,7 @@ inline QString sizeString(qint64 size) return QString("%1 GB").arg(size / (1024 * 1024 * 1024)); } -DownloadItemWidget::DownloadItemWidget(QWebEngineDownloadItem *m_item, QWidget *parent) +DownloadItemWidget::DownloadItemWidget(QWebEngineDownloadRequest *m_item, QWidget *parent) : QWidget(parent) , ui(new Ui::DownloadItemForm) { @@ -51,11 +51,11 @@ DownloadItemWidget::DownloadItemWidget(QWebEngineDownloadItem *m_item, QWidget * ui->path_label->setText(QString("%1<br>%2").arg(item->downloadDirectory(), item->downloadFileName())); this->updateState(item->state()); - connect(item, &QWebEngineDownloadItem::stateChanged, this, &DownloadItemWidget::updateState); - connect(item, &QWebEngineDownloadItem::downloadProgress, this, &DownloadItemWidget::updateProgress); - connect(item, &QWebEngineDownloadItem::finished, this, &DownloadItemWidget::updateFinished); + connect(item, &QWebEngineDownloadRequest::stateChanged, this, &DownloadItemWidget::updateState); + connect(item, &QWebEngineDownloadRequest::receivedBytesChanged, this, &DownloadItemWidget::updateProgress); + connect(item, &QWebEngineDownloadRequest::isFinishedChanged, this, &DownloadItemWidget::updateFinished); - connect(ui->abort_toolButton, &QToolButton::clicked, item, &QWebEngineDownloadItem::cancel); + connect(ui->abort_toolButton, &QToolButton::clicked, item, &QWebEngineDownloadRequest::cancel); connect(ui->pause_toolButton, &QToolButton::clicked, item, [m_item](bool clicked) { clicked ? m_item->pause() : m_item->resume(); }); @@ -69,34 +69,34 @@ DownloadItemWidget::~DownloadItemWidget() delete ui; } -void DownloadItemWidget::updateState(QWebEngineDownloadItem::DownloadState state) +void DownloadItemWidget::updateState(QWebEngineDownloadRequest::DownloadState state) { switch(state) { - case QWebEngineDownloadItem::DownloadRequested: + case QWebEngineDownloadRequest::DownloadRequested: ui->status_label->setText(tr("Requested")); ui->pause_toolButton->setEnabled(true); ui->abort_toolButton->setEnabled(true); ui->open_toolButton->setEnabled(false); break; - case QWebEngineDownloadItem::DownloadInProgress: + case QWebEngineDownloadRequest::DownloadInProgress: ui->status_label->setText(tr("In progress")); ui->pause_toolButton->setEnabled(true); ui->abort_toolButton->setEnabled(true); ui->open_toolButton->setEnabled(false); break; - case QWebEngineDownloadItem::DownloadCompleted: + case QWebEngineDownloadRequest::DownloadCompleted: ui->status_label->setText(tr("Completed")); ui->pause_toolButton->setEnabled(false); ui->abort_toolButton->setEnabled(false); ui->open_toolButton->setEnabled(true); break; - case QWebEngineDownloadItem::DownloadCancelled: + case QWebEngineDownloadRequest::DownloadCancelled: ui->status_label->setText(tr("Cancelled")); ui->pause_toolButton->setEnabled(false); ui->abort_toolButton->setEnabled(false); ui->open_toolButton->setEnabled(false); break; - case QWebEngineDownloadItem::DownloadInterrupted: + case QWebEngineDownloadRequest::DownloadInterrupted: ui->status_label->setText(tr("Interrupted")); ui->pause_toolButton->setEnabled(false); ui->abort_toolButton->setEnabled(false); @@ -107,8 +107,11 @@ void DownloadItemWidget::updateState(QWebEngineDownloadItem::DownloadState state } } -void DownloadItemWidget::updateProgress(qint64 value, qint64 total) +void DownloadItemWidget::updateProgress() { + const auto value = item->receivedBytes(); + const auto total = item->totalBytes(); + ui->progressBar->setValue(static_cast<int>((static_cast<long double>(value) / static_cast<long double>(total)) * 100)); ui->progressBar->setFormat(QString("%1 / %2").arg(sizeString(value), sizeString(total))); } diff --git a/lib/downloads/widgets/downloaditemwidget.h b/lib/downloads/widgets/downloaditemwidget.h index a1de175..5d540a3 100644 --- a/lib/downloads/widgets/downloaditemwidget.h +++ b/lib/downloads/widgets/downloaditemwidget.h @@ -9,7 +9,7 @@ #ifndef SMOLBOTE_DOWNLOADITEMFORM_H #define SMOLBOTE_DOWNLOADITEMFORM_H -#include <QWebEngineDownloadItem> +#include <QWebEngineDownloadRequest> #include <QWidget> namespace Ui @@ -22,17 +22,17 @@ class DownloadItemWidget : public QWidget Q_OBJECT public: - explicit DownloadItemWidget(QWebEngineDownloadItem *m_item, QWidget *parent = 0); + explicit DownloadItemWidget(QWebEngineDownloadRequest *m_item, QWidget *parent = 0); ~DownloadItemWidget() override; private slots: - void updateState(QWebEngineDownloadItem::DownloadState state); - void updateProgress(qint64 value, qint64 total); + void updateState(QWebEngineDownloadRequest::DownloadState state); + void updateProgress(); void updateFinished(); private: Ui::DownloadItemForm *ui; - QWebEngineDownloadItem *item = nullptr; + QWebEngineDownloadRequest *item = nullptr; }; #endif // SMOLBOTE_DOWNLOADITEMFORM_H diff --git a/lib/pluginloader/CMakeLists.txt b/lib/pluginloader/CMakeLists.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/pluginloader/CMakeLists.txt diff --git a/lib/session_formats/CMakeLists.txt b/lib/session_formats/CMakeLists.txt new file mode 100644 index 0000000..c043559 --- /dev/null +++ b/lib/session_formats/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(session_formats STATIC + session_json.cpp session_json.hpp +) +target_link_libraries(session_formats PUBLIC Qt6::Core) +target_include_directories(session_formats PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${PROJECT_SOURCE_DIR}/include) diff --git a/lib/urlfilter/CMakeLists.txt b/lib/urlfilter/CMakeLists.txt new file mode 100644 index 0000000..012839d --- /dev/null +++ b/lib/urlfilter/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library(urlfilter STATIC + matcher.h urlfilter.h + adblock/adblocklist.cpp adblock/adblocklist.h + adblock/parser.cpp adblock/parser.h + hostlist/hostlist.cpp hostlist/hostlist.h +) +target_link_libraries(urlfilter PUBLIC Qt6::WebEngineCore) +target_include_directories(urlfilter PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + + +add_executable(test_urlfilter_matcher test/matcher.cpp) +target_link_libraries(test_urlfilter_matcher PUBLIC urlfilter GTest::gtest_main) +gtest_add_tests(TARGET test_urlfilter_matcher + SOURCES test/matcher.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/test) + +add_executable(test_urlfilter_hostlist test/hostlist.cpp) +target_link_libraries(test_urlfilter_hostlist PUBLIC urlfilter GTest::gtest_main) +gtest_add_tests(TARGET test_urlfilter_hostlist + SOURCES test/hostlist.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/test) + +add_executable(test_urlfilter_adblock test/adblock.cpp) +target_link_libraries(test_urlfilter_adblock PUBLIC urlfilter GTest::gtest_main) +gtest_add_tests(TARGET test_urlfilter_adblock + SOURCES test/adblock.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/test) diff --git a/lib/urlfilter/meson.build b/lib/urlfilter/meson.build deleted file mode 100644 index 2591028..0000000 --- a/lib/urlfilter/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -urlfilter_lib = static_library('urlfilter', - ['urlfilter.h', 'matcher.h', - 'hostlist/hostlist.cpp', 'hostlist/hostlist.h', - 'adblock/adblocklist.cpp', 'adblock/adblocklist.h', 'adblock/parser.cpp', 'adblock/parser.h'], - dependencies: dep_qt5 -) - -dep_urlfilter = declare_dependency( - include_directories: include_directories('.'), - link_with: urlfilter_lib -) - -if dep_gtest.found() - test('urlfilter: matcher', - executable('urlfilter-matcher', dependencies: [dep_qt5, dep_gtest, dep_urlfilter], sources: ['test/matcher.cpp']), - workdir: meson.current_source_dir() / 'test' - ) - test('urlfilter: host list', - executable('urlfilter-hostlist', dependencies: [dep_qt5, dep_gtest, dep_urlfilter], sources: ['test/hostlist.cpp']), - workdir: meson.current_source_dir() / 'test' - ) - test('urlfilter: adblock list', - executable('urlfilter-adblocklist', dependencies: [dep_qt5, dep_gtest, dep_urlfilter], sources: ['test/adblock.cpp']), - workdir: meson.current_source_dir() / 'test' - ) -endif diff --git a/linux/.config b/linux/.config new file mode 100644 index 0000000..2fb8236 --- /dev/null +++ b/linux/.config @@ -0,0 +1,111 @@ + +# +# Application +# +CONFIG_POI_NAME="smolbote" +CONFIG_POI_ICON=":/icons/poi.svg" +CONFIG_POI_CFG_PATH="~/.config/smolbote/smolbote.cfg" +# end of Application + +# +# Keyboard shortcuts +# + +# +# Main Window shortcuts +# +CONFIG_shortcuts.session.save="Ctrl+S,S" +CONFIG_shortcuts.session.open="Ctrl+S,O" +CONFIG_shortcuts.window.newgroup="Ctrl+G" +CONFIG_shortcuts.window.newwindow="Ctrl+N" +CONFIG_shortcuts.window.about="F1" +CONFIG_shortcuts.window.quit="Ctrl+Q" +CONFIG_shortcuts.window.search="F3" +CONFIG_shortcuts.window.downloads.show="Ctrl+D" + +# +# Navigation Bar shortcuts +# +CONFIG_navigationbar.show="Ctrl+Shift+N" +CONFIG_shortcuts.navigation.back="Ctrl+Left" +CONFIG_shortcuts.navigation.backmenu="Ctrl+Down" +CONFIG_shortcuts.navigation.forward="Ctrl+Right" +CONFIG_shortcuts.navigation.forwardmenu="Ctrl+Up" +CONFIG_shortcuts.navigation.refresh="F5" +CONFIG_shortcuts.navigation.reload="Ctrl+F5" +CONFIG_shortcuts.navigation.home="Ctrl+Home" + +# +# Address Bar shortcuts +# +CONFIG_shortcuts.address.focus="F4" +CONFIG_shortcuts.address.menu="F2" + +# +# Subwindow shortcuts +# +CONFIG_shortcuts.subwindow.close="Ctrl+Shift+W" +CONFIG_shortcuts.subwindow.fullscreen="F11" +CONFIG_shortcuts.subwindow.newtab="Ctrl+T" +CONFIG_shortcuts.subwindow.closetab="Ctrl+W" +CONFIG_shortcuts.subwindow.restoretab="Ctrl+Shift+T" +CONFIG_shortcuts.subwindow.tableft="Ctrl+O" +CONFIG_shortcuts.subwindow.movetableft="Ctrl+Shift+O" +CONFIG_shortcuts.subwindow.tabright="Ctrl+P" +CONFIG_shortcuts.subwindow.movetabright="Ctrl+Shift+P" +# end of Keyboard shortcuts + +# +# Main Window +# +CONFIG_mainwindow.title="smolbote" +CONFIG_mainwindow.width=1280 +CONFIG_mainwindow.height=720 +# end of Main Window + +# +# Bookmarks +# +CONFIG_bookmarks.path="~/.config/smolbote/bookmarks.xbel" +CONFIG_shortcuts.window.bookmarks.show="Ctrl+B" +CONFIG_bookmarks.toolbar.show="Ctrl+Shift+B" +# CONFIG_bookmarks.toolbar.movable is not set +# CONFIG_bookmarks.toolbar.visible is not set +# end of Bookmarks + +# +# Profile Settings +# +CONFIG_profile.path="~/.config/smolbote/profiles.d" +CONFIG_profile.default="default" +CONFIG_profile.search="https://duckduckgo.com/?q=%1&ia=web" +CONFIG_profile.homepage="about:blank" +CONFIG_profile.newtab="about:blank" +# end of Profile Settings + +CONFIG_USEPLUGINS=y + +# +# Plugin Settings +# +CONFIG_PLUGINS_PATH="~/.config/smolbote/plugins.d" +# CONFIG_PLUGINS_SIGNATURE_IGNORED is not set +CONFIG_PLUGINS_SIGNATURE_CHECKED=y +# CONFIG_PLUGINS_SIGNATURE_ENFORCED is not set +CONFIG_PLUGINS_SIGNATURE_HASH="SHA256" +# end of Plugin Settings + +# +# Default paths +# +CONFIG_filter.path="~/.config/smolbote/hosts.d" +CONFIG_downloads.path="~/Downloads" +CONFIG_session.path="~/.config/smolbote/session.d" +# CONFIG_USEPLASMA is not set +# CONFIG_USEBREAKPAD is not set + +# +# Workarounds +# +CONFIG_QTBUG_65223=y +# end of Workarounds diff --git a/linux/config.yaml b/linux/config.yaml new file mode 100644 index 0000000..38e6e6e --- /dev/null +++ b/linux/config.yaml @@ -0,0 +1,63 @@ +--- +- poi: + name: smolbote + icon: :/icons/poi.svg + config: ~/.config/smolbote/smolbote.cfg + +- shortcuts: + session.save: Ctrl+S,S + session.open: Ctrl+S,O + + window.newgroup: Ctrl+G + window.newwindow: Ctrl+N + window.about: F1 + window.quit: Ctrl+Q + window.search: F3 + window.bookmarks.show: Ctrl+B + window.downloads.show: Ctrl+D + + navigationbar.show: Ctrl+Shift+N + navigation.back: Ctrl+Left + navigation.backmenu: Ctrl+Down + navigation.forward: Ctrl+Right + navigation.forwardmenu: Ctrl+Up + navigation.refresh: F5 + navigation.reload: Ctrl+F5 + navigation.home: Ctrl+Home + + address.focus: F4 + address.menu: F2 + + subwindow.close: Ctrl+Shift+W + subwindow.fullscreen: F11 + subwindow.newtab: Ctrl+T + subwindow.closetab: Ctrl+W + subwindow.restoretab: Ctrl+Shift+T + subwindow.tableft: Ctrl+O + subwindow.movetableft: Ctrl+Shift+O + subwindow.tabright: Ctrl+P + subwindow.movetabright: Ctrl+Shift+P + +- mainwindow: + title: smolbote + width: 1280 + height: 720 + +- bookmarks: + path: ~/.config/smolbote/bookmarks.xbel + toolbar.show: Ctrl+Shift+B + toolbar.movable: false + toolbar.visible: true + +- downloads: + path: ~/Downloads + +- profile: + path: ~/.config/smolbote/profiles.d + default: default + search: https://duckduckgo.com/?q=%1&ia=web + homepage: about:blank + newtab: about:blank + +- session: + path: ~/.config/smolbote/session.d diff --git a/linux/firejail/poi.profile b/linux/firejail/poi.profile index a7d3005..1a644d7 100644 --- a/linux/firejail/poi.profile +++ b/linux/firejail/poi.profile @@ -1,21 +1,22 @@ # Firejail profile for poi # This file is overwritten after every install/update # Persistent local customizations -include /etc/firejail/poi.local +include poi.local # Persistent global definitions -include /etc/firejail/globals.local +include globals.local # noblacklist: exclude from blacklist noblacklist ${HOME}/.cache/smolbote noblacklist ${HOME}/.config/smolbote noblacklist ${HOME}/.local/share/smolbote -include /etc/firejail/disable-common.inc -include /etc/firejail/disable-devel.inc -include /etc/firejail/disable-interpreters.inc -include /etc/firejail/disable-passwdmgr.inc -include /etc/firejail/disable-programs.inc -include /etc/firejail/disable-xdg.inc +include disable-common.inc +include disable-devel.inc +include disable-exec.inc +include disable-interpreters.inc +include disable-passwdmgr.inc +include disable-programs.inc +include disable-xdg.inc mkdir ${HOME}/.cache/smolbote mkdir ${HOME}/.config/smolbote @@ -25,7 +26,7 @@ whitelist ${DOWNLOADS} whitelist ${HOME}/.cache/smolbote whitelist ${HOME}/.config/smolbote whitelist ${HOME}/.local/share/smolbote -include /etc/firejail/whitelist-common.inc +include whitelist-common.inc ## caps.drop all - Removes the ability to call programs usually run only by root. Ex - chown, setuid @@ -43,7 +44,9 @@ caps.drop all netfilter ## nodbus - Disable access to dbus. -nodbus +#nodbus +dbus-user none +dbus-system none ## nodvd - Disable access to optical disk drives. nodvd @@ -60,6 +63,9 @@ noroot ## notv - Disable access to DVB TV devices. notv +## nou2f - Disable access to U2F devices. +nou2f + # novideo - Disable access to video devices. novideo @@ -67,20 +73,16 @@ novideo protocol unix,inet,inet6,netlink ## seccomp - Blacklists a large swath of syscalls from being accessible. -#seccomp -## Use seccomp.drop for now as seccomp is broken with many programs. -seccomp.drop @clock,@cpu-emulation,@module,@obsolete,@privileged,@raw-io,@reboot,@resources,@swap,ptrace -# QtWebEngine require chroot syscall on AMD CPUS and/or ATI Graphics for some bizarre reason -# Use the following seccomp.drop instead on such systems. -#seccomp.drop @clock,@cpu-emulation,@module,@obsolete,@raw-io,@reboot,@resources,@swap,ptrace,mount,umount2,pivot_root +# QtWebEngine requires chroot syscall on AMD and ATI Graphics for some bizarre reason +seccomp !name_to_handle_at,!chroot ## shell - Run the program directly, without a user shell. # breaks secondary instances when using join-or-start after shell=none -#shell none +shell none ## tracelog - Log all viloations to syslog. -# tracelog segfaults QtWebEngine on AMD CPUS and/or ATI Graphics for some bizarre reason -tracelog +# tracelog segfaults QtWebEngine on AMD and ATI Graphics for some bizarre reason +#tracelog ## disable-mnt - Deny access to /mnt, /media, /run/mount, and /run/media disable-mnt @@ -88,7 +90,7 @@ disable-mnt ## private-bin - Creates a virtual /bin directory containing only temporary copies of the following executables. # bash required to launch from kde kickoff menu # breaks if installed to /usr/local -#private-bin bash,poi +private-bin bash,poi ## private-dev - Create a virtual /dev directory. Only dri, null, full, zero, tty, pts, ptmx, random, snd, urandom, video, log and shm devices are available. private-dev @@ -101,12 +103,6 @@ private-etc fonts,group,machine-id,resolv.conf # breaks SingleApplication without join-or-start set private-tmp - -## noexec - Prevent execution of files in the specified locations -noexec ${HOME} -noexec /tmp - - # join-or-start - Join the sandbox identified by name or start a new one join-or-start poi diff --git a/linux/makepkg/PKGBUILD b/linux/makepkg/PKGBUILD index 5e27542..efdea15 100644 --- a/linux/makepkg/PKGBUILD +++ b/linux/makepkg/PKGBUILD @@ -1,53 +1,43 @@ # Maintainer: Aqua-sama <aqua at iserlohn-fortress dot net> ## not-use flags -# Enable plugin signing: -_signPlugins=0 -# Enable breakpad integraton: -_enableBreakpad=0 # install prefix -_prefix='/usr/local' +_prefix='/usr' pkgname=smolbote-git pkgdesc='Yet another no-frills browser' pkgver=0 pkgrel=1 -url="https://neueland.iserlohn-fortress.net/gitea/smolbote" +url="https://neueland.iserlohn-fortress.net/cgit/smolbote" install="smolbote.install" arch=('x86_64' 'aarch64') license=('GPL3') -depends=('qt5-webengine>=5.11.0' 'spdlog') -makedepends=('git' 'meson' 'python-kconfiglib' 'openssl' 'qt5-tools' 'scdoc' 'catch2') -if [ $_enableBreakpad == "1" ]; then - makedepends+=('breakpad-git') -fi +depends=('qt6-svg' 'qt6-webengine' 'spdlog' 'fmt') +makedepends=('git' 'cmake' 'openssl' 'qt6-tools' 'scdoc') optdepends=('firejail: launch a sandboxed instance') -# this isn't a hard requirement, simply a workaround as the build script -# sets some additional hardening flags that the default makepkg.conf -# will turn down -options=(!buildflags) - # use git+file:///path/to/your/repo to build from a local repo -source=("git+https://library.iserlohn-fortress.net/aqua/smolbote.git" - "https://neueland.iserlohn-fortress.net/releases/SingleApplication-3.1.1a.tar.xz"{,.sig} - "https://neueland.iserlohn-fortress.net/releases/args.hxx-6.2.2.tar.xz"{,.sig}) +source=("git+https://neueland.iserlohn-fortress.net/cgit/smolbote" + "SingleApplication-v3.4.1.tar.gz::https://github.com/itay-grudev/SingleApplication/archive/refs/tags/v3.4.1.tar.gz") b2sums=('SKIP' - 'cec3de8dbf252bfa6dc488e5a1440695f4dd3abffdf30948b7d1a3df3d9c85911e981c802ed5a882f1407315114529f4016e55c7d05fbbd1dafe5495b0a63f4a' - 'SKIP' - '440e357006883fbf1b1a796051500a6b068858a35947cd1119767bed8e0a86a7db4aff16498934d7217c375fe643da03c22007e438f30899e247153f25c922b6' - 'SKIP') + 'fe320ccb0390b13b1c7b0c017cff34b02f5138bd6643457843a7200374c8a994a37a1b00a65c70e83ba5bdc61f157ccfaa8cdfc0eee7b2149df4acda06173669') -validgpgkeys=(BB1C090188E3E32B375C13FD095DE26BC16D2E98) # Aqua-sama <aqua@iserlohn-fortress.net> +validpgpkeys=(BB1C090188E3E32B375C13FD095DE26BC16D2E98) # Aqua-sama <aqua@iserlohn-fortress.net> prepare() { - mkdir "$srcdir/smolbote/subprojects/packagecache/" - ln -s "$srcdir/SingleApplication-3.1.1a" "$srcdir/smolbote/subprojects/" - ln -s "$srcdir/args.hxx-6.2.2" "$srcdir/smolbote/subprojects/" + msg2 "Populate third_party packages..." + mkdir "$srcdir/smolbote/third_party" + ln -s "$srcdir/SingleApplication-3.4.1" "$srcdir/smolbote/third_party/SingleApplication" + + msg2 "Configure..." + cmake -S $srcdir/smolbote -B $srcdir/build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DINSTALL_MANPAGES=ON } pkgver() { @@ -57,61 +47,15 @@ pkgver() { } build() { - mkdir -p $srcdir/build - cd $srcdir/smolbote - KCONFIG_CONFIG=linux/.config alldefconfig - - # For a list of configureable options, check smolbote/meson_options.txt, or - # run `meson configure` in $srcdir/build - - # --wrap-mode=nodownload - disable meson from downloading dependency wraps. This will cause it to fail if makedepends are not found by pkg-config or cmake. - # --wrap-mode=nofallback - disable downloads as a fallback too. Implies --wrap-mode=nodownload - # --buildtype=plain - meson won't add any flags to the command line - # --prefix=... - install prefix - # --auto-features=disabled - features should be explicitly enabled - # b_pie: Build executables as position independent - # b_lto: Use link time optimization - meson --buildtype=release --wrap-mode=nodownload \ - --prefix=$_prefix --auto-features=disabled \ - -Db_pie=true -Ddefault_library=static \ - -Dmanpage=enabled \ - $srcdir/build - - if [ $_enableBreakpad == "1" ]; then - msg2 "Enabling crashhandler" - meson configure -Ddebug=true -Dcrashhandler=enabled - KCONFIG_CONFIG=linux/.config setconfig USEBREAKPAD=y - fi - - # Run menuconfig - #KCONFIG_CONFIG=linux/.config menuconfig - - # Build - ninja -C $srcdir/build "$MAKEFLAGS" + cmake --build $srcdir/build -- ${MAKEFLAGS} + cmake --build $srcdir/build --target manpages -- ${MAKEFLAGS} } check() { - ninja -C $srcdir/build test + ctest --test-dir $srcdir/build } package() { - # Install - cd $srcdir/build - DESTDIR="$pkgdir" ninja install - - if [ $_signPlugins == "1" ]; then - msg "Signing plugins" - for so in $pkgdir/$_prefix/lib/smolbote/plugins/*.so; do - openssl dgst -sha256 -sign $srcdir/build/lib/pluginloader/privateKey.pem -out $so.sig $so - msg2 "Signed $(basename $so)" - done - fi - - if [ $_enableBreakpad == "1" ]; then - msg "Installing debug symbols" - ninja -C $srcdir/build linux/poi.sym - install -dm644 $pkgdir/$_prefix/lib/smolbote/symbols/poi/$(head -n1 linux/poi.sym | awk '{ print $(NF-1) }') - install -m644 -t $pkgdir/$_prefix/lib/smolbote/symbols/poi/$(head -n1 linux/poi.sym | awk '{ print $(NF-1) }') $srcdir/build/linux/poi.sym - fi + DESTDIR="$pkgdir" cmake --install $srcdir/build --strip } diff --git a/meson.build b/meson.build deleted file mode 100644 index bc72ff3..0000000 --- a/meson.build +++ /dev/null @@ -1,147 +0,0 @@ -project('smolbote', ['cpp'], - version: '0.1.0', - default_options: ['cpp_std=c++2a', 'warning_level=3'], - license: 'GPL3', - meson_version: '>=0.52.0' -) - -kconfig = import('unstable-kconfig') -kconf = kconfig.load(host_machine.system() + '/.config') - -cdata = configuration_data(kconf) - -version_h = vcs_tag( - command: [find_program('git').path(), 'describe', '--long', '--abbrev=40'], - #fallback: defaults to meson.project_version(), - input: 'include/version.h.in', - output: 'version.h' -) - -# add -DQT_NO_DEBUG to non-debug builds -if not get_option('debug') - add_project_arguments('-DQT_NO_DEBUG', language: 'cpp') -endif - -sourceset = import('sourceset') - -cxx = meson.get_compiler('cpp') - -# add some specific flags -add_project_arguments(cxx.get_supported_arguments([ - '-ffunction-sections', # Place each function into its own section, better ASLR but larger executables - '-fstack-protector-all', # Emit code to check for buffer overflows on all functions - '-fstack-clash-protection', # Emit code to check for stack clash attacks - - # gcc specific - '-fconcepts', # gcc9 c++20 compat - - # clang specific - '-mspeculative-load-hardening', # Spectre v1 mitigation - '-Xclang -plugin-arg-clazy -Xclang level0,level1', # clazy default warning level - - ## warnings - # variables - '-Wunused', # warn on anything being unused - '-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 - '-Werror=redundant-decls', - '-Woverloaded-virtual', # warn if you overload (not override) a virtual function - '-Werror=return-type', - # style - '-Wformat=2', # security issues around printf - '-Wdate-time', # __TIME__ and __DATE__ macros - '-Werror=missing-field-initializers', - # objects - '-Wnull-dereference', - '-Wconsumed', # use-after-move warnings - '-Wlifetime', # object lifetime issues - # logic - '-Wlogical-op', # logical operations being used where bitwise were probably wanted - '-Wimplicit-fallthrough', - '-Wduplicated-cond', # if/else chain has duplicated conditions - '-Wduplicated-brances', # if/else branches have duplicated code - # casts - '-Wold-style-cast', - '-Wcast-align', # potential performance problem casts - '-Wuseless-cast', # cast to same type - '-Wconversion', # type conversions that may lose data - '-Wsign-conversion', # sign conversions - '-Wdouble-promotion', # float is promoted to double - # others - '-Werror=pedantic', # if non-standard c++ is used - #'-Weffc++', -]), language: 'cpp') - -if get_option('buildtype') == 'release' - add_project_arguments(cxx.get_supported_arguments([ - '-flto=4', - ]), language: 'cpp') -endif - -mod_qt5 = import('qt5') -dep_qt5 = dependency('qt5', - modules: [ 'Core', 'Network', 'Widgets', 'Svg', 'WebEngine', 'WebEngineWidgets', 'Concurrent' ], - include_type: 'system' -) - -dep_spdlog = dependency('spdlog', fallback: ['spdlog', 'spdlog_dep'], version: '>=1.3.1') - -optional_deps = [] -poi_cpp_args = [] - -dep_breakpad = dependency('breakpad-client', include_type: 'system', required: get_option('crashhandler')) -dep_threads = dependency('threads', include_type: 'system', required: get_option('crashhandler')) -if dep_breakpad.found() - poi_cpp_args += '-DHAVE_BREAKPAD' -endif - -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() - -subdir('include') # plugin interaces - -subdir('lib/bookmarks') -subdir('lib/configuration') -subdir('lib/downloads') -subdir('lib/pluginloader') -subdir('lib/urlfilter') -subdir('lib/session_formats') - -subdir('src') -subdir('lang') -subdir('doc') -subdir('tools') - -subdir('plugins/ProfileEditor') - -subdir('test/firefox-bookmarks-json-parser') - -ssconfig = poi_sourceset.apply(cdata) - -poi_exe = executable(get_option('poi'), - cpp_args: ['-DQAPPLICATION_CLASS=QApplication', poi_cpp_args], - sources: [ssconfig.sources()], - include_directories: [ plugininterfaces_include, include_directories('src') ], - dependencies: [ dep_qt5, dep_spdlog, dep_SingleApplication, dep_args, optional_deps, dep_bookmarks, dep_configuration, dep_downloads, dep_pluginloader, dep_urlfilter, ssconfig.dependencies(), lib_session_formats ], - install: true, -) - -test('poi-bookmarks: xbel', poi_exe, args: [ 'bookmarks', '-x', files('test/bookmarks.xbel'), '--export=stdout' ]) - -subdir(host_machine.system()) - -# cppcheck target -cppcheck = find_program('cppcheck', required: false) -if cppcheck.found() -run_target('cppcheck', - command: [cppcheck, '--enable=all', '--project=' + meson.build_root() / 'compile_commands.json'] -) -endif diff --git a/scripts/gen-default-cfg.py b/scripts/gen-default-cfg.py deleted file mode 100755 index dc88ceb..0000000 --- a/scripts/gen-default-cfg.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import argparse -import kconfiglib - -def node_to_str(node): - if(node.item.type == kconfiglib.STRING): - return "std::string(\"" + node.item.str_value + "\")" - elif(node.item.type == kconfiglib.INT): - return "int(" + node.item.str_value + ")" - elif(node.item.type == kconfiglib.BOOL): - tri_val_str = ("false", "unknown", "true")[node.item.tri_value] - return "bool(" + tri_val_str + ")" - - return None - -def writeItem(node, file): - while node: - if isinstance(node.item, kconfiglib.Symbol): - name = node.item.name.lower().replace('_', '.') - print("\t{ \"" + name + "\", " + node_to_str(node) + " },", file=file) - - if node.list: - writeItem(node.list, file) - - node = node.next - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--kconfig", nargs="?", default="Kconfig", help="Top-level Kconfig") - parser.add_argument("--dotconfig", nargs="?", help=".config") - parser.add_argument("--input", type=argparse.FileType('r'), help="settings.h.in") - parser.add_argument("--output", type=argparse.FileType('w'), default=sys.stdout, help="Output location") - args = parser.parse_args() - - kconf = kconfiglib.Kconfig(args.kconfig) - if args.dotconfig is not None: - kconf.load_config(args.dotconfig) - - print("/* Autogenerated file", file=args.output) - print(" * kconfig: {}".format(args.kconfig), file=args.output) - print(" * dotconfig: {}".format(args.dotconfig), file=args.output) - print(" */", file=args.output) - - for line in args.input: - if "@__DEFAULT_CFG__" in line: - writeItem(kconf.top_node.list, file=args.output) - else: - print(line, end='', file=args.output) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f80dbc7 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,69 @@ +# version.h +configure_file(version.h.in version.h) + +add_executable(poi + applicationmenu.cpp + applicationmenu.h + bookmarks/bookmarksform.ui + bookmarks/bookmarkstoolbar.cpp + bookmarks/bookmarkstoolbar.h + bookmarks/bookmarkswidget.cpp + bookmarks/bookmarkswidget.h + #bookmarks/builtins.cpp + bookmarks/editbookmarkdialog.cpp + bookmarks/editbookmarkdialog.h + bookmarks/editbookmarkdialog.ui + browser.cpp + browser.h + #builtins.cpp + #builtins.h + #crashhandler.cpp + #crashhandler.h + #crashhandler_dummy.cpp + main.cpp + mainwindow/addressbar.cpp + mainwindow/addressbar.h + mainwindow/addressbar.ui + mainwindow/mainwindow.cpp + mainwindow/mainwindow.h + mainwindow/menubar.cpp + mainwindow/menubar.h + mainwindow/widgets/completer.cpp + mainwindow/widgets/completer.h + mainwindow/widgets/dockwidget.cpp + mainwindow/widgets/dockwidget.h + mainwindow/widgets/menusearch.cpp + mainwindow/widgets/menusearch.h + mainwindow/widgets/navigationbar.cpp + mainwindow/widgets/navigationbar.h + mainwindow/widgets/searchform.cpp + mainwindow/widgets/searchform.h + mainwindow/widgets/searchform.ui + mainwindow/widgets/urllineedit.cpp + mainwindow/widgets/urllineedit.h + session/savesessiondialog.cpp + session/savesessiondialog.h + session/savesessiondialog.ui + session/sessiondialog.cpp + session/sessiondialog.h + session/sessiondialog.ui + subwindow/subwindow.cpp + subwindow/subwindow.h + subwindow/tabwidget.cpp + subwindow/tabwidget.h + util.cpp + util.h + wallet/wallet.cpp + wallet/wallet.h + + # resources + ${PROJECT_SOURCE_DIR}/data/resources.qrc +) +target_include_directories(poi PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR}) +target_enable_warnings(poi) +target_link_libraries(poi + PRIVATE SingleApplication::SingleApplication spdlog::spdlog + PRIVATE bookmarks configuration downloads session_formats urlfilter + PRIVATE about webengine +) + diff --git a/src/about/CMakeLists.txt b/src/about/CMakeLists.txt new file mode 100644 index 0000000..f1e2c41 --- /dev/null +++ b/src/about/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(about STATIC + aboutdialog.cpp aboutdialog.h aboutdialog.ui + aboutplugin.cpp aboutplugin.h aboutplugin.ui +) +target_link_libraries(about PUBLIC Qt6::Widgets Qt6::SvgWidgets) +target_include_directories(about PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +# tests +add_executable(test_about test/main.cpp) +target_link_libraries(test_about PRIVATE about) +add_test(NAME about_dialog COMMAND $<TARGET_FILE:test_about> -platform offscreen) +set_property(TEST about_dialog PROPERTY ENVIRONMENT autoclose=1) diff --git a/src/about/aboutdialog.cpp b/src/about/aboutdialog.cpp index 894b1ec..1013866 100644 --- a/src/about/aboutdialog.cpp +++ b/src/about/aboutdialog.cpp @@ -8,7 +8,6 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" -#include <version.h> // compiler // clang also defines __GNUC__, so we need to check for clang first @@ -59,7 +58,7 @@ AboutDialog::AboutDialog(QWidget *parent) "<p>This program is distributed in the hope that it will be useful, but without any warranty.</p>" "<p>You can read the full terms of the license on <a href='https://www.gnu.org/licenses/gpl-3.0.en.html'>the GNU website</a>.</p>")); - ui->detailsLabel->setText(tr("<p>Version " poi_Version "</p>" + ui->detailsLabel->setText(tr("<p>Version %1</p>" "<p>Compiled with " compiler "</p>" "<p>Libraries: <ul>" "<li><a href='https://www.qt.io/'>Qt5</a> " QT_VERSION_STR "</li>" @@ -69,7 +68,8 @@ AboutDialog::AboutDialog(QWidget *parent) #ifdef CONFIG_USEBREAKPAD "<li><a href='https://chromium.googlesource.com/breakpad/breakpad'>Breakpad</a></li>" #endif - "</ul></p>")); + "</ul></p>") + .arg(qApp->applicationVersion())); } AboutDialog::~AboutDialog() diff --git a/src/about/meson.build b/src/about/meson.build deleted file mode 100644 index 49274a8..0000000 --- a/src/about/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -about_moc = mod_qt5.preprocess( - moc_headers: ['aboutdialog.h', 'aboutplugin.h'], - ui_files: ['aboutdialog.ui', 'aboutplugin.ui'], - dependencies: dep_qt5 -) - -dep_about = declare_dependency( - include_directories: '.', - link_with: static_library('about', - [ 'aboutdialog.cpp', 'aboutplugin.cpp', about_moc, version_h], - dependencies: [dep_qt5]) -) - -poi_sourceset.add(dep_about) - -test('about dialog', executable('about', - sources: 'test/main.cpp', - dependencies: [ dep_qt5, dep_about, dep_catch ]), - args: [ '-platform', 'offscreen' ], - env: 'autoclose=1' -) diff --git a/src/applicationmenu.cpp b/src/applicationmenu.cpp index dc888e7..aff5576 100644 --- a/src/applicationmenu.cpp +++ b/src/applicationmenu.cpp @@ -22,31 +22,31 @@ ApplicationMenu::ApplicationMenu(Browser *app, QWidget *parent) setTitle(qApp->applicationName()); Configuration conf; - const auto sessionPath = conf.value<QString>("session.path").value(); + const auto sessionPath = conf.value<QString>("session/path").value(); auto *actionSaveSession = addAction(tr("Save Session"), this, [sessionPath]() { auto *sessionDialog = new SaveSessionDialog(nullptr); if(sessionDialog->exec() == QDialog::Accepted) { sessionDialog->save(sessionPath); } }); - conf.shortcut<QAction>(*actionSaveSession, "shortcuts.session.save"); + conf.shortcut<QAction>(*actionSaveSession, "shortcuts/session.save"); auto *actionOpenSession = addAction(tr("Open Session"), this, []() { auto *sessionDialog = new SessionDialog(nullptr); sessionDialog->exec(); }); - conf.shortcut<QAction>(*actionOpenSession, "shortcuts.session.open"); + conf.shortcut<QAction>(*actionOpenSession, "shortcuts/session.open"); bottom_pluginSeparator = addSeparator(); auto *actionAbout = addAction(tr("About"), app, &Browser::about); - conf.shortcut<QAction>(*actionAbout, "shortcuts.window.about"); + conf.shortcut<QAction>(*actionAbout, "shortcuts/window.about"); auto *action_aboutPlugins = addAction(tr("About Plugins"), app, &Browser::aboutPlugins); - conf.shortcut(*action_aboutPlugins, "app.shortcuts.about.plugins"); + conf.shortcut(*action_aboutPlugins, "shortcuts/about.plugins"); auto *actionQuit = addAction(tr("Quit"), app, &Browser::quit); - conf.shortcut<QAction>(*actionQuit, "shortcuts.window.quit"); + conf.shortcut<QAction>(*actionQuit, "shortcuts/window.quit"); } void ApplicationMenu::addPlugin(QPluginLoader *plugin) diff --git a/src/bookmarks/bookmarkstoolbar.cpp b/src/bookmarks/bookmarkstoolbar.cpp index 52dcae2..3174874 100644 --- a/src/bookmarks/bookmarkstoolbar.cpp +++ b/src/bookmarks/bookmarkstoolbar.cpp @@ -18,10 +18,10 @@ BookmarksToolbar::BookmarksToolbar(const BookmarkModel *model, MainWindow *paren m_window = parent; Configuration conf; - setObjectName("bookmarks.toolbar"); + setObjectName("bookmarks/toolbar"); setWindowTitle(tr("Bookmarks Toolbar")); - setMovable(conf.value<bool>("bookmarks.toolbar.movable").value()); - setVisible(conf.value<bool>("bookmarks.toolbar.visible").value()); + setMovable(conf.value<bool>("bookmarks/toolbar.movable").value()); + setVisible(conf.value<bool>("bookmarks/toolbar.visible").value()); setToolButtonStyle(Qt::ToolButtonTextBesideIcon); const auto *root = model->root(); diff --git a/src/browser.cpp b/src/browser.cpp index f748e2f..74da540 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -31,7 +31,7 @@ #include <QTimer> #include <QTranslator> #include <QVersionNumber> -#include <pluginloader.h> +// #include <pluginloader.h> #include <spdlog/spdlog.h> #include <version.h> @@ -40,11 +40,11 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary) { Configuration conf; - setApplicationName(conf.value<QString>("poi.name").value()); - setWindowIcon(QIcon(conf.value<QString>("poi.icon").value())); + setApplicationName(conf.value<QString>("poi/name").value()); + setWindowIcon(QIcon(conf.value<QString>("poi/icon").value())); setApplicationVersion(QVersionNumber::fromString(QLatin1String(poi_Version)).toString()); - if(const auto _translation = conf.value<QString>("browser.translation")) { + if(const auto _translation = conf.value<QString>("browser/translation")) { auto *translator = new QTranslator(this); if(translator->load(_translation.value())) installTranslator(translator); @@ -52,7 +52,7 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary) delete translator; } - if(const auto _locale = conf.value<QString>("browser.locale")) { + if(const auto _locale = conf.value<QString>("browser/locale")) { auto *locale = new QTranslator(this); if(locale->load("qt_" + _locale.value(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) installTranslator(locale); @@ -60,11 +60,11 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary) delete locale; } - if(auto iconTheme = conf.value<QString>("browser.iconTheme")) { + if(auto iconTheme = conf.value<QString>("browser/iconTheme")) { QIcon::setThemeName(iconTheme.value()); } - if(auto stylesheet = conf.value<QString>("browser.stylesheet")) { + if(auto stylesheet = conf.value<QString>("browser/stylesheet")) { QFile f(stylesheet.value()); if(f.open(QIODevice::ReadOnly)) { setStyleSheet(f.readAll()); @@ -74,11 +74,11 @@ Browser::Browser(int &argc, char *argv[], bool allowSecondary) // 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(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(); m_profileManager = std::make_unique<WebProfileManager<false>>(profiles, default_id, search, homepage, newtab); m_profileManager->make_global(); @@ -92,13 +92,13 @@ 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>(conf.value<QString>("downloads/path").value()); m_profileManager->walk([this](const QString &, WebProfile *profile, QSettings *) { 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>(QString::fromStdString(conf.value<std::string>("bookmarks/path").value())); connect(m_bookmarks.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) { m_windows.last()->createTab(url); }); @@ -142,22 +142,31 @@ bool Browser::loadPlugin(const QString &path) } Configuration conf; + bool loaded = false; + +#ifdef HAVE_PLUGINLOADER const auto state = PluginLoader::signature_state( conf.value<bool>("plugins.signature.ignored").value(), conf.value<bool>("plugins.signature.checked").value(), conf.value<bool>("plugins.signature.enforced").value()); auto *loader = new PluginLoader(path, state, this); - const bool loaded = loader->load(); + loaded = loader->load(); +#endif if(!loaded) { +#ifdef HAVE_PLUGINLOADER delete loader; +#endif return false; } +#ifdef HAVE_PLUGINLOADER auto *info = new PluginInfo(loader); m_plugins.append(info); emit pluginAdded(loader); +#endif + return true; } diff --git a/src/main.cpp b/src/main.cpp index dad1f73..8cd1d34 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,36 +7,15 @@ */ #include "browser.h" -#include "builtins.h" #include "configuration.h" -#include "crashhandler.h" #include "session/sessiondialog.h" #include "session_json.hpp" -#include "settings.h" -#include "util.h" -#include "version.h" +#include <QCommandLineParser> #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 = ", ") -{ - 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 main(int argc, char **argv) { // change log pattern @@ -45,104 +24,71 @@ int main(int argc, char **argv) spdlog::set_level(spdlog::level::debug); // Set global log level to debug #endif - const command_map commands{ - { "configuration", builtins::configuration }, - { "bookmarks", builtins::bookmarks }, - }; - - 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 - spdlog::debug("Loading configuration {}", init_conf(args::get(cmd_config))); - - 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)); + QCommandLineParser parser; + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("url", "URLs to open"); + + // generic options + QCommandLineOption cmd_config({ "c", "config" }, "Set the configuration file.", "path"); + parser.addOption(cmd_config); + + QCommandLineOption cmd_session({ "s", "session" }, "Open the specified session.", "path"); + parser.addOption(cmd_session); + + QCommandLineOption cmd_pick_session("pick-session", "Show all available sessions and select which one to open."); + parser.addOption(cmd_pick_session); + + // Qt options + QCommandLineOption cmd_renderer("renderer", "Select the OpenGL renderer used by the application. Available options are: desktop, gles, software", "value"); + parser.addOption(cmd_renderer); + + // SingleApplication options + QCommandLineOption cmd_no_remote("no-remote", "Start a new instance that won't accept or send remote commands."); + parser.addOption(cmd_no_remote); + + { // handle command line options that need to be set before the QApplication object is created + QStringList args; + for(int i = 0; i < argc; ++i) + args.append(argv[i]); + parser.parse(args); + + // the OpenGL implementation needs to be manually set to software if it's not properly configured + if(parser.isSet(cmd_renderer)) { + const auto value = parser.value(cmd_renderer); + if(value == QLatin1String("desktop")) { + QApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); + } else if(value == QLatin1String("gles")) { + QApplication::setAttribute(Qt::AA_UseOpenGLES, true); + } else if(value == QLatin1String("software")) { + QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL, true); + } else { + spdlog::error("Unknown cmd_renderer flag: {}", qUtf8Printable(value)); } } - - } catch(args::Help &) { - std::cout << parser; - return 0; - - } catch(args::Error &e) { - std::cerr << e.what() << std::endl; - std::cerr << parser; - return -1; } - // argc, argv, allowSecondary - Browser app(argc, argv); // set this, otherwise the webview becomes black when using a stylesheet - app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); - - { - Configuration conf; - - if(conf.value<bool>("usebreakpad").value()) { - CrashHandler::Context ctx( - conf.value<std::string>("path.crashdump").value(), - conf.value<std::string>("path.crashhandler").value()); + QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); - if(CrashHandler::install_handler(ctx)) { - spdlog::info("Installed breakpad crash handler: {}", ctx.dumppath); - } else { - spdlog::warn("Failed to install breakpad crash handler: {}", ctx.dumppath); - } - } + spdlog::info("Loaded configuration: {}", Configuration::init_global(parser.value(cmd_config).toStdString())); - // 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 { - spdlog::warn("Failed loading plugin [{}]", qUtf8Printable(path)); - } - } - } + Browser app(argc, argv); + parser.process(app); const auto profile = []() { - Configuration c; - return c.value<QString>("profile.default").value(); + Configuration conf; + return conf.value<QString>("profile/default").value(); }(); - QStringList urls; - for(const auto &u : args::get(cmd_args)) { - urls.append(QString::fromStdString(u)); - } + QStringList urls = parser.positionalArguments(); if(urls.isEmpty()) { urls.append(QString()); } // 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) { + if(app.isPrimary() && !parser.isSet(cmd_no_remote)) { + QObject::connect(&app, &Browser::receivedMessage, &app, [&app](quint32 instanceId, const QByteArray &message) { Q_UNUSED(instanceId); JsonSession session(message); app.open(session.get()); @@ -151,13 +97,13 @@ int main(int argc, char **argv) { const auto session = [&]() { - if(cmd_session) { - QFile sessionJson(QString::fromStdString(args::get(cmd_session))); + if(parser.isSet(cmd_session)) { + QFile sessionJson(parser.value(cmd_session)); if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) { return JsonSession(sessionJson.readAll()); } } - if(cmd_pickSession) { + if(parser.isSet(cmd_pick_session)) { SessionDialog dlg; if(const auto pick = dlg.pickSession()) { return JsonSession(pick.value()); @@ -166,13 +112,13 @@ int main(int argc, char **argv) return JsonSession(profile, urls); }(); - if(app.isPrimary() || cmd_noRemote) { + if(app.isPrimary() || parser.isSet(cmd_no_remote)) { app.open(session.get()); } else { // app is secondary and not standalone - return app.sendMessage(session.serialize()); + return app.sendMessage(session.serialize()) ? EXIT_SUCCESS : EXIT_FAILURE; } } - return app.exec(); + return QApplication::exec(); } diff --git a/src/mainwindow/addressbar.cpp b/src/mainwindow/addressbar.cpp index d37a6a7..ed1cb3e 100644 --- a/src/mainwindow/addressbar.cpp +++ b/src/mainwindow/addressbar.cpp @@ -19,9 +19,9 @@ AddressBar::AddressBar(QWidget *parent) ui->setupUi(this); Configuration conf; - ui->urlBar->menuAction->setShortcut(QKeySequence(conf.value<QString>("shortcuts.address.menu").value())); + ui->urlBar->menuAction->setShortcut(QKeySequence(conf.value<QString>("shortcuts/address.menu").value())); - auto *focusShortcut = new QShortcut(QKeySequence(conf.value<QString>("shortcuts.address.focus").value()), parent); + auto *focusShortcut = new QShortcut(QKeySequence(conf.value<QString>("shortcuts/address.focus").value()), parent); connect(focusShortcut, &QShortcut::activated, ui->urlBar, [this]() { ui->urlBar->setFocus(); ui->urlBar->selectAll(); diff --git a/src/mainwindow/mainwindow.cpp b/src/mainwindow/mainwindow.cpp index 64c149c..bd5d0c2 100644 --- a/src/mainwindow/mainwindow.cpp +++ b/src/mainwindow/mainwindow.cpp @@ -30,16 +30,16 @@ MainWindow::MainWindow(const Session::MainWindow &mainwindow_data, QMenu *appMen Configuration config; // create UI - defaultWindowTitle = config.value<QString>("mainwindow.title").value(); + defaultWindowTitle = config.value<QString>("mainwindow/title").value(); setWindowTitle(defaultWindowTitle); - resize(config.value<int>("mainwindow.width").value(), config.value<int>("mainwindow.height").value()); - if(config.value<bool>("mainwindow.maximized").value_or(false)) { + resize(config.value<int>("mainwindow/width").value(), config.value<int>("mainwindow/height").value()); + if(config.value<bool>("mainwindow/maximized").value_or(false)) { setWindowState(Qt::WindowMaximized); } show(); navigationToolBar = new NavigationBar(this); - navigationToolBar->setMovable(config.value<bool>("navigation.movable").value_or(false)); + navigationToolBar->setMovable(config.value<bool>("navigation/movable").value_or(false)); addToolBar(Qt::TopToolBarArea, navigationToolBar); navigationToolBar->connectWebView(nullptr); diff --git a/src/mainwindow/menubar.cpp b/src/mainwindow/menubar.cpp index 5fc039a..e479e8d 100644 --- a/src/mainwindow/menubar.cpp +++ b/src/mainwindow/menubar.cpp @@ -79,30 +79,30 @@ MenuBar::MenuBar(QMenu *appMenu, MainWindow *parent) auto *actionBookmarks = window->addAction(tr("Bookmarks"), browser, [browser, parent]() { browser->showWidget(browser->bookmarks(), parent); }); - conf.shortcut<QAction>(*actionBookmarks, "shortcuts.window.bookmarks.show"); + conf.shortcut<QAction>(*actionBookmarks, "shortcuts/window.bookmarks.show"); auto *actionDownloads = window->addAction(tr("Downloads"), browser, [browser, parent]() { browser->showWidget(browser->downloads(), parent); }); - conf.shortcut<QAction>(*actionDownloads, "shortcuts.window.downloads.show"); + conf.shortcut<QAction>(*actionDownloads, "shortcuts/window.downloads.show"); window->addSeparator(); auto *actionNewWindow = window->addAction(tr("New Window"), browser, [browser]() { const Session::MainWindow window_data; browser->open({ window_data }, false); }); - conf.shortcut<QAction>(*actionNewWindow, "shortcuts.window.newwindow"); + conf.shortcut<QAction>(*actionNewWindow, "shortcuts/window.newwindow"); auto *actionNewSubwindow = window->addAction(tr("New Subwindow"), parent, [parent]() { const Session::SubWindow session; parent->createSubWindow(session); }); - conf.shortcut<QAction>(*actionNewSubwindow, "shortcuts.window.newgroup"); + conf.shortcut<QAction>(*actionNewSubwindow, "shortcuts/window.newgroup"); auto *actionCloseSubwindow = window->addAction(tr("Close Subwindow"), parent, [parent]() { parent->currentSubWindow()->close(); }); - conf.shortcut<QAction>(*actionCloseSubwindow, "shortcuts.subwindow.close"); + conf.shortcut<QAction>(*actionCloseSubwindow, "shortcuts/subwindow.close"); window->addSeparator()->setText(tr("Toolbars")); @@ -140,7 +140,7 @@ MenuBar::MenuBar(QMenu *appMenu, MainWindow *parent) _subwindow->setCurrentTab(index); }); }); - conf.shortcut<QAction>(*actionNewTab, "shortcuts.subwindow.newtab"); + conf.shortcut<QAction>(*actionNewTab, "shortcuts/subwindow.newtab"); subwindow->addSeparator(); @@ -150,7 +150,7 @@ MenuBar::MenuBar(QMenu *appMenu, MainWindow *parent) _subwindow->setCurrentTab(index); }); }); - conf.shortcut<QAction>(*actionRestoreTab, "shortcuts.subwindow.restoretab"); + conf.shortcut<QAction>(*actionRestoreTab, "shortcuts/subwindow.restoretab"); auto *restoreTabsMenu = subwindow->addMenu(tr("Restore previous tab")); connect(restoreTabsMenu, &QMenu::aboutToShow, parent, [parent, restoreTabsMenu]() { @@ -200,28 +200,28 @@ MenuBar::MenuBar(QMenu *appMenu, MainWindow *parent) _subwindow->setCurrentTab(qMax(0, currentIdx - 1)); }); }); - conf.shortcut<QAction>(*leftTab, "shortcuts.subwindow.tableft"); + conf.shortcut<QAction>(*leftTab, "shortcuts/subwindow.tableft"); auto *moveTabLeft = subwindow->addAction(tr("Move tab left"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->moveTab(currentIdx, currentIdx - 1); }); }); - conf.shortcut<QAction>(*moveTabLeft, "shortcuts.subwindow.movetableft"); + conf.shortcut<QAction>(*moveTabLeft, "shortcuts/subwindow.movetableft"); auto *rightTab = subwindow->addAction(tr("Switch to tab on the right"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->setCurrentTab(qMin(currentIdx + 1, _subwindow->tabCount() - 1)); }); }); - conf.shortcut<QAction>(*rightTab, "shortcuts.subwindow.tabright"); + conf.shortcut<QAction>(*rightTab, "shortcuts/subwindow.tabright"); auto *moveTabRight = subwindow->addAction(tr("Move tab right"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { _subwindow->moveTab(currentIdx, currentIdx + 1); }); }); - conf.shortcut<QAction>(*moveTabRight, "shortcuts.subwindow.movetabright"); + conf.shortcut<QAction>(*moveTabRight, "shortcuts/subwindow.movetabright"); subwindow->addSeparator(); @@ -230,7 +230,7 @@ MenuBar::MenuBar(QMenu *appMenu, MainWindow *parent) _subwindow->closeTab(currentIdx); }); }); - conf.shortcut<QAction>(*closeTab, "shortcuts.subwindow.closetab"); + conf.shortcut<QAction>(*closeTab, "shortcuts/subwindow.closetab"); subwindow->addAction(tr("Close tabs to the left"), parent, [parent]() { run_if(parent->currentSubWindow(), [](SubWindow *_subwindow, int currentIdx) { @@ -329,10 +329,7 @@ MenuBar::MenuBar(QMenu *appMenu, MainWindow *parent) auto *printer = new QPrinter(QPrinterInfo::defaultPrinter()); QPrintDialog dlg(printer, parent); if(dlg.exec() == QDialog::Accepted) { - parent->currentView()->page()->print(printer, [printer](bool success) { - Q_UNUSED(success); - delete printer; - }); + parent->currentView()->print(printer); } } }); diff --git a/src/mainwindow/widgets/navigationbar.cpp b/src/mainwindow/widgets/navigationbar.cpp index 799ebec..6da6f6c 100644 --- a/src/mainwindow/widgets/navigationbar.cpp +++ b/src/mainwindow/widgets/navigationbar.cpp @@ -29,7 +29,7 @@ NavigationBar::NavigationBar(QWidget *parent) // Back button backAction = addAction(Util::icon(QStyle::SP_ArrowBack), tr("Back")); - config.shortcut<QAction>(*backAction, "shortcuts.navigation.back"); + config.shortcut<QAction>(*backAction, "shortcuts/navigation.back"); connect(backAction, &QAction::triggered, this, [this]() { m_view->history()->back(); }); @@ -47,7 +47,7 @@ NavigationBar::NavigationBar(QWidget *parent) }); backAction->setMenu(backMenu); - auto *backMenuShortcut = new QShortcut(QKeySequence(config.value<QString>("shortcuts.navigation.backmenu").value()), this); + auto *backMenuShortcut = new QShortcut(QKeySequence(config.value<QString>("shortcuts/navigation.backmenu").value()), this); connect(backMenuShortcut, &QShortcut::activated, backMenu, [this, backMenu]() { if(backAction->isEnabled()) { auto *widget = this->widgetForAction(backAction); @@ -57,7 +57,7 @@ NavigationBar::NavigationBar(QWidget *parent) // Forward button forwardAction = addAction(Util::icon(QStyle::SP_ArrowForward), tr("Forward")); - config.shortcut<QAction>(*forwardAction, "shortcuts.navigation.forward"); + config.shortcut<QAction>(*forwardAction, "shortcuts/navigation.forward"); connect(forwardAction, &QAction::triggered, this, [this]() { m_view->history()->forward(); }); @@ -75,7 +75,7 @@ NavigationBar::NavigationBar(QWidget *parent) }); forwardAction->setMenu(forwardMenu); - auto *forwardMenuShortcut = new QShortcut(QKeySequence(config.value<QString>("shortcuts.navigation.forwardmenu").value()), this); + auto *forwardMenuShortcut = new QShortcut(QKeySequence(config.value<QString>("shortcuts/navigation.forwardmenu").value()), this); connect(forwardMenuShortcut, &QShortcut::activated, forwardMenu, [this, forwardMenu]() { if(forwardAction->isEnabled()) { auto *widget = this->widgetForAction(forwardAction); @@ -85,7 +85,7 @@ NavigationBar::NavigationBar(QWidget *parent) // Stop/Refresh button stopReloadAction = addAction(Util::icon(QStyle::SP_BrowserReload), tr("Refresh")); - config.shortcut<QAction>(*stopReloadAction, "shortcuts.navigation.refresh"); + config.shortcut<QAction>(*stopReloadAction, "shortcuts/navigation.refresh"); connect(stopReloadAction, &QAction::triggered, this, [this]() { if(m_view->isLoaded()) m_view->reload(); @@ -93,14 +93,14 @@ NavigationBar::NavigationBar(QWidget *parent) m_view->stop(); }); - auto *reloadShortcut = new QShortcut(QKeySequence(config.value<QString>("shortcuts.navigation.reload").value()), this); + auto *reloadShortcut = new QShortcut(QKeySequence(config.value<QString>("shortcuts/navigation.reload").value()), this); connect(reloadShortcut, &QShortcut::activated, this, [this]() { m_view->triggerPageAction(QWebEnginePage::ReloadAndBypassCache); }); // Home button homeAction = addAction(Util::icon(QStyle::SP_DirHomeIcon), tr("Home")); - config.shortcut<QAction>(*homeAction, "shortcuts.navigation.home"); + config.shortcut<QAction>(*homeAction, "shortcuts/navigation.home"); connect(homeAction, &QAction::triggered, this, [this]() { m_view->load(m_view->profile()->homepage()); }); diff --git a/src/mainwindow/widgets/searchform.cpp b/src/mainwindow/widgets/searchform.cpp index e10933b..af54f5c 100644 --- a/src/mainwindow/widgets/searchform.cpp +++ b/src/mainwindow/widgets/searchform.cpp @@ -10,6 +10,7 @@ #include "ui_searchform.h" #include <QHideEvent> #include <QShowEvent> +#include <QWebEngineFindTextResult> #include <QWebEngineView> SearchForm::SearchForm(QWidget *parent) @@ -26,7 +27,8 @@ SearchForm::SearchForm(QWidget *parent) QWebEnginePage::FindFlags searchFlags; searchFlags.setFlag(QWebEnginePage::FindCaseSensitively, ui->caseSensitivity_checkBox->isChecked()); searchFlags.setFlag(QWebEnginePage::FindBackward, ui->reverseSearch_checkBox->isChecked()); - m_view->findText(ui->lineEdit->text(), searchFlags, [this](bool found) { + m_view->findText(ui->lineEdit->text(), searchFlags, [this](const QWebEngineFindTextResult &result) { + const bool found = result.numberOfMatches() > 0; ui->result_label->setVisible(!found); }); } diff --git a/src/meson.build b/src/meson.build deleted file mode 100644 index 6a4abd8..0000000 --- a/src/meson.build +++ /dev/null @@ -1,60 +0,0 @@ -python = import('python') -python3 = python.find_installation('python3') - -poi_settings_h = custom_target('default_config_value', - input: files('../scripts/gen-default-cfg.py', '../Kconfig', '..'/host_machine.system()/'.config', 'settings.h.in'), - output: 'settings.h', - command: [ python3, '@INPUT0@', '--kconfig=@INPUT1@', '--dotconfig=@INPUT2@', '--input=@INPUT3@', '--output=@OUTPUT@' ] -) - -subdir('about') -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' ], - qresources: '../data/resources.qrc', - rcc_extra_arguments: ['--format-version=1'], - dependencies: dep_qt5 -)) - -poi_sourceset.add(files( - 'main.cpp', 'builtins.cpp', - 'browser.cpp', 'applicationmenu.cpp', - 'util.cpp', 'util.h', - - 'mainwindow/mainwindow.cpp', - 'mainwindow/addressbar.cpp', - 'mainwindow/menubar.cpp', - 'mainwindow/widgets/completer.cpp', - 'mainwindow/widgets/urllineedit.cpp', - 'mainwindow/widgets/dockwidget.cpp', - 'mainwindow/widgets/menusearch.cpp', - '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', - - 'subwindow/subwindow.cpp', - 'subwindow/tabwidget.cpp', - - 'wallet/wallet.cpp', 'wallet/wallet.h' -), - version_h, poi_settings_h -) - -poi_sourceset.add(when: [dep_breakpad, dep_threads], - if_true: files('crashhandler.cpp'), - if_false: files('crashhandler_dummy.cpp') -) - diff --git a/src/session/sessiondialog.h b/src/session/sessiondialog.h index 0a04940..fc31aa4 100644 --- a/src/session/sessiondialog.h +++ b/src/session/sessiondialog.h @@ -11,6 +11,7 @@ #include "session.hpp" #include <QDialog> +#include <optional> namespace Ui { diff --git a/src/settings.h.in b/src/settings.h.in deleted file mode 100644 index 88fbcf5..0000000 --- a/src/settings.h.in +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include <QStandardPaths> -#include <configuration.h> -#include <fstream> - -inline const std::string init_conf(const std::string &path) -{ - auto value_map = std::make_unique<Configuration, std::initializer_list<std::pair<std::string, conf_value_t>>>({ - @__DEFAULT_CFG__ - }); - - const std::string cfgpath = [&]() { - if(path.empty()) - return value_map->value<std::string>("poi.cfg.path").value(); - - auto p = path; - if(p.front() == '~') - p.replace(0, 1, QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()); - return p; - }(); - - std::fstream fs; - fs.open(cfgpath, std::fstream::in); - if(fs.is_open()) { - value_map->read(fs); - fs.close(); - } - - Configuration::move_global(std::move(value_map)); - return cfgpath; -} diff --git a/src/subwindow/subwindow.cpp b/src/subwindow/subwindow.cpp index 6e9a713..a2d6138 100644 --- a/src/subwindow/subwindow.cpp +++ b/src/subwindow/subwindow.cpp @@ -57,7 +57,6 @@ SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags flags) close(); } else { auto *view = dynamic_cast<WebView *>(tabWidget->widget(index)); - Q_CHECK_PTR(view); disconnect(titleConnection); disconnect(linkHoveredConnection); @@ -67,12 +66,10 @@ SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags flags) this->setWindowTitle(QString("%1 :%2").arg(title, v->profile()->name())); }); setWindowTitle(QString("%1 :%2").arg(view->title(), view->profile()->name())); - linkHoveredConnection = connect(view->page(), &WebPage::linkHovered, this, [this](const QString &url) { if(!url.isEmpty()) emit showStatusMessage(url, 3000); }); - emit currentViewChanged(view); } }); diff --git a/src/subwindow/tabwidget.cpp b/src/subwindow/tabwidget.cpp index 6f1e348..69f3b8a 100644 --- a/src/subwindow/tabwidget.cpp +++ b/src/subwindow/tabwidget.cpp @@ -42,8 +42,11 @@ TabWidget::TabWidget(SubWindow *parent) // when changing tabs, give focus to the widget // otherwise when closing tabs, the tabwidget will retain focus connect(this, &TabWidget::currentChanged, this, [this](int index) { - if(widget(index)) + previous = current; + current = index; + /*if(widget(index)) { widget(index)->setFocus(); + }*/ }); // context menu @@ -68,12 +71,6 @@ TabWidget::TabWidget(SubWindow *parent) removeTab(i); } }); - - // - connect(this, &TabWidget::currentChanged, this, [this](int index) { - previous = current; - current = index; - }); } TabWidget::~TabWidget() @@ -85,7 +82,9 @@ TabWidget::~TabWidget() int TabWidget::addTab(WebView *view) { - Q_ASSERT_X(view != nullptr, "TabWidget::addTab", "Tried to add null view"); + if(view == nullptr) { + return -1; + } const int idx = QTabWidget::addTab(view, view->title()); connect(view, &WebView::titleChanged, [this, view](const QString &title) { @@ -100,7 +99,6 @@ int TabWidget::addTab(WebView *view) setTabIcon(current_idx, icon); } }); - tabBar()->setTabData(idx, QVariant::fromValue<SubWindow::TabData>(SubWindow::TabData{})); return idx; } diff --git a/src/util.cpp b/src/util.cpp index fe74175..3061c96 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -62,7 +62,7 @@ QIcon Util::icon(QStyle::StandardPixmap id) { return QIcon::fromTheme("go-home", qApp->style()->standardIcon(id)); default: - spdlog::warn("FIXME: unhandled StandardPixmap {}", id); + spdlog::warn("FIXME: unhandled StandardPixmap"); return qApp->style()->standardIcon(id); } } diff --git a/include/version.h.in b/src/version.h.in index 44f10d9..b387811 100644 --- a/include/version.h.in +++ b/src/version.h.in @@ -3,6 +3,6 @@ // output of 'git describe --long --abbrev=40': // x.y.z-0-g0123456789012345678901234567890123456789 -#define poi_Version "@VCS_TAG@" +#define poi_Version "@CMAKE_PROJECT_VERSION@" #endif // SMOLBOTE_VERSION_H diff --git a/src/webengine/CMakeLists.txt b/src/webengine/CMakeLists.txt new file mode 100644 index 0000000..704a6d0 --- /dev/null +++ b/src/webengine/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(webengine STATIC + urlinterceptor.cpp urlinterceptor.h + webpage.cpp webpage.h + webprofile.cpp webprofile.h + webprofilemanager.cpp webprofilemanager.h + webviewcontextmenu.cpp webviewcontextmenu.h + webview.cpp webview.h +) +target_link_libraries(webengine PUBLIC Qt6::WebEngineCore Qt6::WebEngineWidgets) +target_include_directories(webengine PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${PROJECT_SOURCE_DIR}/include) diff --git a/src/webengine/webpage.cpp b/src/webengine/webpage.cpp index b2b19b5..f8a8f7f 100644 --- a/src/webengine/webpage.cpp +++ b/src/webengine/webpage.cpp @@ -62,6 +62,7 @@ WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) connect(this, &QWebEnginePage::featurePermissionRequested, this, &WebPage::featurePermissionDialog); connect(this, &QWebEnginePage::renderProcessTerminated, this, &WebPage::renderProcessCrashed); + connect(this, &QWebEnginePage::certificateError, this, &WebPage::certificateError); } bool WebPage::certificateError(const QWebEngineCertificateError &certificateError) @@ -77,9 +78,8 @@ bool WebPage::certificateError(const QWebEngineCertificateError &certificateErro messageBox.setText(tr("An SSL error has occurred on <strong>%1</strong>").arg(certificateError.url().toString())); messageBox.setInformativeText(tr("<p>%1</p>" "<p>This error %2 be overridden.</p>") - .arg(certificateError.errorDescription(), + .arg(certificateError.description(), certificateError.isOverridable() ? tr("can") : tr("cannot"))); - messageBox.setDetailedText(tr("Error code: %1").arg(certificateError.error())); if(certificateError.isOverridable()) { messageBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Abort); diff --git a/src/webengine/webpage.h b/src/webengine/webpage.h index 91ae4f3..bd7d54c 100644 --- a/src/webengine/webpage.h +++ b/src/webengine/webpage.h @@ -19,12 +19,10 @@ public: WebPage(QWebEngineProfile *profile, QObject *parent = nullptr); ~WebPage() override = default; -protected: - bool certificateError(const QWebEngineCertificateError &certificateError) override; - protected slots: void featurePermissionDialog(const QUrl &securityOrigin, QWebEnginePage::Feature feature); void renderProcessCrashed(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode); + bool certificateError(const QWebEngineCertificateError &certificateError); }; #endif // SMOLBOTE_WEBPAGE_H diff --git a/src/webengine/webprofile.cpp b/src/webengine/webprofile.cpp index 719ab34..f1e71fb 100644 --- a/src/webengine/webprofile.cpp +++ b/src/webengine/webprofile.cpp @@ -18,12 +18,10 @@ static WebProfile *s_profile = nullptr; void WebProfile::setDefaultProfile(WebProfile *profile) { - Q_CHECK_PTR(profile); s_profile = profile; } WebProfile *WebProfile::defaultProfile() { - Q_CHECK_PTR(s_profile); return s_profile; } diff --git a/src/webengine/webprofilemanager.cpp b/src/webengine/webprofilemanager.cpp index 5cc83f8..9ae2960 100644 --- a/src/webengine/webprofilemanager.cpp +++ b/src/webengine/webprofilemanager.cpp @@ -8,6 +8,7 @@ #include "webprofilemanager.h" #include "webprofile.h" +#include <QActionGroup> static WebProfileManager<false> *s_instance = nullptr; diff --git a/src/webengine/webprofilemanager.h b/src/webengine/webprofilemanager.h index 91dcaf8..a356506 100644 --- a/src/webengine/webprofilemanager.h +++ b/src/webengine/webprofilemanager.h @@ -41,10 +41,11 @@ public: } if(!profiles.contains(default_id)) { - Profile profile; - profile.settings = WebProfile::load(QString(), search, homepage, newtab); - profile.ptr = WebProfile::load(default_id, profile.settings, true); - profiles[default_id] = profile; + auto *settings = WebProfile::load(QString(), search, homepage, newtab); + profiles[default_id] = Profile{ + .settings = settings, + .ptr = WebProfile::load(default_id, settings, true), + }; } WebProfile::setDefaultProfile(profiles[default_id].ptr); } @@ -73,7 +74,7 @@ public: } if(profile != nullptr && settings != nullptr) { - profiles[id] = Profile{ profile, settings, false }; + profiles[id] = Profile{ settings, profile, false }; } } @@ -101,8 +102,8 @@ private: set_typestate(consumed) void consume() {} struct Profile { - WebProfile *ptr = nullptr; QSettings *settings = nullptr; + WebProfile *ptr = nullptr; bool selfDestruct = false; }; diff --git a/src/webengine/webview.cpp b/src/webengine/webview.cpp index 38e564a..bc52102 100644 --- a/src/webengine/webview.cpp +++ b/src/webengine/webview.cpp @@ -37,9 +37,7 @@ WebView::WebView(WebProfile *profile, cb_createWindow_t cb, QWidget *parent) : WebView(parent) { cb_createWindow = cb; - Q_CHECK_PTR(profile); - m_profile = profile; - setPage(new WebPage(profile, this)); + setProfile(profile); } WebView::WebView(const Session::WebView &webview_data, cb_createWindow_t cb, QWidget *parent) @@ -47,11 +45,7 @@ WebView::WebView(const Session::WebView &webview_data, cb_createWindow_t cb, QWi { cb_createWindow = cb; WebProfileManager profileManager; - - auto *profile = profileManager.profile(webview_data.profile); - if(profile != nullptr) { - setProfile(profile); - } + setProfile(profileManager.profile(webview_data.profile)); if(!webview_data.url.isEmpty()) load(QUrl::fromUserInput(webview_data.url)); @@ -64,9 +58,9 @@ WebView::WebView(const Session::WebView &webview_data, cb_createWindow_t cb, QWi void WebView::setProfile(WebProfile *profile) { - m_profile = profile; + m_profile = (profile == nullptr) ? WebProfile::defaultProfile() : profile; const auto url = this->url(); - setPage(new WebPage(profile, this)); + setPage(new WebPage(m_profile, this)); this->load(url); } diff --git a/src/webengine/webviewcontextmenu.cpp b/src/webengine/webviewcontextmenu.cpp index ea5e8c6..0de3b9f 100644 --- a/src/webengine/webviewcontextmenu.cpp +++ b/src/webengine/webviewcontextmenu.cpp @@ -17,7 +17,7 @@ #include <QStyle> #include <QToolButton> #include <QVBoxLayout> -#include <QWebEngineContextMenuData> +#include <QWebEngineContextMenuRequest> #include <QWebEngineHistory> #include <QWidgetAction> @@ -94,100 +94,7 @@ WebViewContextMenu::WebViewContextMenu(WebView *view) this->addAction(navButtons); this->addSeparator(); - const auto ctxdata = view->page()->contextMenuData(); - - if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeNone) { - auto *backMenu = this->addMenu(tr("Back")); - if(!view->history()->canGoBack()) { - backMenu->setEnabled(false); - } else { - connect(backMenu, &QMenu::aboutToShow, view, [view, backMenu]() { - backMenu->clear(); - const auto backItems = view->history()->backItems(10); - for(const QWebEngineHistoryItem &item : backItems) { - backMenu->addAction(historyAction(view, item)); - } - }); - } - - auto *forwardMenu = this->addMenu(tr("Forward")); - if(!view->history()->canGoForward()) { - forwardMenu->setEnabled(false); - } else { - connect(forwardMenu, &QMenu::aboutToShow, view, [view, forwardMenu]() { - forwardMenu->clear(); - const auto forwardItems = view->history()->forwardItems(10); - for(const QWebEngineHistoryItem &item : forwardItems) { - forwardMenu->addAction(historyAction(view, item)); - } - }); - } - - connect(this->addAction(tr("Reload")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::Reload); - }); - connect(this->addAction(tr("Reload and bypass Cache")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::ReloadAndBypassCache); - }); - - this->addSeparator(); - - connect(this->addAction(tr("Select All")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::SelectAll); - }); - connect(this->addAction(tr("Clear Selection")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::Unselect); - }); - connect(this->addAction(tr("Copy to clipboard")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::Copy); - }); - - } else if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeImage) { - connect(this->addAction(tr("Copy image to clipboard")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::CopyImageToClipboard); - }); - connect(this->addAction(tr("Copy image URL to clipboard")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::CopyImageUrlToClipboard); - }); - if(!ctxdata.mediaUrl().isEmpty()) { - if(view->url() != ctxdata.mediaUrl()) { - connect(this->addAction(tr("Open image")), &QAction::triggered, view, [view, ctxdata]() { - view->load(ctxdata.mediaUrl()); - }); - connect(this->addAction(tr("Open image in new tab")), &QAction::triggered, view, [view, ctxdata]() { - view->createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.mediaUrl()); - }); - } - connect(this->addAction(tr("Save image")), &QAction::triggered, view, [view, ctxdata]() { - view->page()->download(ctxdata.mediaUrl()); - }); - } - - } else { - addMenu(view->page()->createStandardContextMenu()); - } - - if(!ctxdata.linkUrl().isEmpty()) { - this->addSeparator(); - connect(this->addAction(tr("Open link in new tab")), &QAction::triggered, view, [view, ctxdata]() { - view->createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.linkUrl()); - }); - - auto *newTabMenu = this->addMenu(tr("Open link in new tab with profile")); - profileMenu(newTabMenu, [view, ctxdata](WebProfile *profile) { - auto *v = view->createWindow(QWebEnginePage::WebBrowserTab); - v->setProfile(profile); - v->load(ctxdata.linkUrl()); - }); - - connect(this->addAction(tr("Open link in new window")), &QAction::triggered, view, [view, ctxdata]() { - view->createWindow(QWebEnginePage::WebBrowserWindow)->load(ctxdata.linkUrl()); - }); - - connect(this->addAction(tr("Copy link address")), &QAction::triggered, view, [view]() { - view->page()->triggerAction(QWebEnginePage::CopyLinkToClipboard); - }); - } + addMenu(view->createStandardContextMenu()); // zoom widget { 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/subprojects/catch2.wrap b/subprojects/catch2.wrap deleted file mode 100644 index 2e20085..0000000 --- a/subprojects/catch2.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = Catch2-2.11.3 - -source_url = https://github.com/catchorg/Catch2/archive/v2.11.3.zip -source_filename = Catch2-2.11.3.zip -source_hash = c5a0a7510379c6f37f70b329986a335a7b8489d67ac417ce8f4262d0cae4cc5d - -patch_url = https://wrapdb.mesonbuild.com/v1/projects/catch2/2.11.3/1/get_zip -patch_filename = catch2-2.11.3-1-wrap.zip -patch_hash = 63c09cb68280435040ad304b3dd87ecfe69dbc216608991d0a82569a63119e57 diff --git a/subprojects/singleapplication.wrap b/subprojects/singleapplication.wrap deleted file mode 100644 index ac817db..0000000 --- a/subprojects/singleapplication.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = SingleApplication-3.1.1a - -source_url = https://neueland.iserlohn-fortress.net/releases/SingleApplication-3.1.1a.tar.xz -source_filename = SingleApplication-3.1.1a.tar.xz -source_hash = df21800c9f3b254048ed34f6cfe96e5a540dc8ab4533b327a6982a6030f77080 diff --git a/subprojects/spdlog.wrap b/subprojects/spdlog.wrap deleted file mode 100644 index b0f760b..0000000 --- a/subprojects/spdlog.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = spdlog-1.4.2 - -source_url = https://github.com/gabime/spdlog/archive/v1.4.2.tar.gz -source_filename = v1.4.2.tar.gz -source_hash = 821c85b120ad15d87ca2bc44185fa9091409777c756029125a02f81354072157 - -patch_url = https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.4.2/1/get_zip -patch_filename = spdlog-1.4.2-1-wrap.zip -patch_hash = e0616f2a956670b0b23daba08f14d4f51a9551e74071269bc218cd05b666b229 |