diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/3rd-party/toml/LICENSE | 24 | ||||
-rw-r--r-- | src/3rd-party/toml/toml.h | 1958 | ||||
-rw-r--r-- | src/browser.cpp | 222 | ||||
-rw-r--r-- | src/browser.h | 59 | ||||
-rw-r--r-- | src/forms/aboutdialog.cpp | 64 | ||||
-rw-r--r-- | src/forms/aboutdialog.h | 4 | ||||
-rw-r--r-- | src/forms/profilesdialog.cpp | 22 | ||||
-rw-r--r-- | src/forms/profilesdialog.h | 2 | ||||
-rw-r--r-- | src/main.cpp | 175 | ||||
-rw-r--r-- | src/mainwindow.cpp | 52 | ||||
-rw-r--r-- | src/mainwindow.h | 8 | ||||
-rw-r--r-- | src/singleapplication.cpp | 8 | ||||
-rw-r--r-- | src/singleapplication.h | 2 | ||||
-rw-r--r-- | src/webengine/webengineprofile.cpp | 157 | ||||
-rw-r--r-- | src/webengine/webengineprofile.h | 7 | ||||
-rw-r--r-- | src/widgets/mainwindowmenubar.cpp | 58 | ||||
-rw-r--r-- | src/widgets/mainwindowmenubar.h | 8 | ||||
-rw-r--r-- | src/widgets/webviewtabbar.cpp | 7 |
18 files changed, 377 insertions, 2460 deletions
diff --git a/src/3rd-party/toml/LICENSE b/src/3rd-party/toml/LICENSE deleted file mode 100644 index 1514196..0000000 --- a/src/3rd-party/toml/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2014, MAYAH -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/src/3rd-party/toml/toml.h b/src/3rd-party/toml/toml.h deleted file mode 100644 index ec8c489..0000000 --- a/src/3rd-party/toml/toml.h +++ /dev/null @@ -1,1958 +0,0 @@ -#ifndef TINYTOML_H_ -#define TINYTOML_H_ - -#include <algorithm> -#include <cassert> -#include <cctype> -#include <chrono> -#include <cmath> -#include <cstdint> -#include <cstdio> -#include <ctime> -#include <iomanip> -#include <istream> -#include <sstream> -#include <stdexcept> -#include <string> -#include <map> -#include <memory> -#include <utility> -#include <vector> - -namespace toml { - -// ---------------------------------------------------------------------- -// Declarations - -class Value; -typedef std::chrono::system_clock::time_point Time; -typedef std::vector<Value> Array; -typedef std::map<std::string, Value> Table; - -namespace internal { -template<typename T> struct call_traits_value { - typedef T return_type; -}; -template<typename T> struct call_traits_ref { - typedef const T& return_type; -}; -} // namespace internal - -template<typename T> struct call_traits; -template<> struct call_traits<bool> : public internal::call_traits_value<bool> {}; -template<> struct call_traits<int> : public internal::call_traits_value<int> {}; -template<> struct call_traits<int64_t> : public internal::call_traits_value<int64_t> {}; -template<> struct call_traits<double> : public internal::call_traits_value<double> {}; -template<> struct call_traits<std::string> : public internal::call_traits_ref<std::string> {}; -template<> struct call_traits<Time> : public internal::call_traits_ref<Time> {}; -template<> struct call_traits<Array> : public internal::call_traits_ref<Array> {}; -template<> struct call_traits<Table> : public internal::call_traits_ref<Table> {}; - -// A value is returned for std::vector<T>. Not reference. -// This is because a fresh vector is made. -template<typename T> struct call_traits<std::vector<T>> : public internal::call_traits_value<std::vector<T>> {}; - -// Formatting flags -enum FormatFlag { - FORMAT_NONE = 0, - FORMAT_INDENT = 1 -}; - -class Value { -public: - enum Type { - NULL_TYPE, - BOOL_TYPE, - INT_TYPE, - DOUBLE_TYPE, - STRING_TYPE, - TIME_TYPE, - ARRAY_TYPE, - TABLE_TYPE, - }; - - Value() : type_(NULL_TYPE), null_(nullptr) {} - Value(bool v) : type_(BOOL_TYPE), bool_(v) {} - Value(int v) : type_(INT_TYPE), int_(v) {} - Value(int64_t v) : type_(INT_TYPE), int_(v) {} - Value(double v) : type_(DOUBLE_TYPE), double_(v) {} - Value(const std::string& v) : type_(STRING_TYPE), string_(new std::string(v)) {} - Value(const char* v) : type_(STRING_TYPE), string_(new std::string(v)) {} - Value(const Time& v) : type_(TIME_TYPE), time_(new Time(v)) {} - Value(const Array& v) : type_(ARRAY_TYPE), array_(new Array(v)) {} - Value(const Table& v) : type_(TABLE_TYPE), table_(new Table(v)) {} - Value(std::string&& v) : type_(STRING_TYPE), string_(new std::string(std::move(v))) {} - Value(Array&& v) : type_(ARRAY_TYPE), array_(new Array(std::move(v))) {} - Value(Table&& v) : type_(TABLE_TYPE), table_(new Table(std::move(v))) {} - - Value(const Value& v); - Value(Value&& v) noexcept; - Value& operator=(const Value& v); - Value& operator=(Value&& v) noexcept; - - // Guards from unexpected Value construction. - // Someone might use a value like this: - // toml::Value v = x->find("foo"); - // But this is wrong. Without this constructor, - // value will be unexpectedly initialized with bool. - Value(const void* v) = delete; - ~Value(); - - // Retruns Value size. - // 0 for invalid value. - // The number of inner elements for array or table. - // 1 for other types. - size_t size() const; - bool empty() const; - Type type() const { return type_; } - - bool valid() const { return type_ != NULL_TYPE; } - template<typename T> bool is() const; - template<typename T> typename call_traits<T>::return_type as() const; - - friend bool operator==(const Value& lhs, const Value& rhs); - friend bool operator!=(const Value& lhs, const Value& rhs) { return !(lhs == rhs); } - - // ---------------------------------------------------------------------- - // For integer/floating value - - // Returns true if the value is int or double. - bool isNumber() const; - // Returns number. Convert to double. - double asNumber() const; - - // ---------------------------------------------------------------------- - // For Time value - - // Converts to time_t if the internal value is Time. - // We don't have as<std::time_t>(). Since time_t is basically signed long, - // it's something like a method to converting to (normal) integer. - std::time_t as_time_t() const; - - // ---------------------------------------------------------------------- - // For Table value - template<typename T> typename call_traits<T>::return_type get(const std::string&) const; - Value* set(const std::string& key, const Value& v); - // Finds a Value with |key|. |key| can contain '.' - // Note: if you would like to find a child value only, you need to use findChild. - const Value* find(const std::string& key) const; - Value* find(const std::string& key); - bool has(const std::string& key) const { return find(key) != nullptr; } - bool erase(const std::string& key); - - Value& operator[](const std::string& key); - - // Merge table. Returns true if succeeded. Otherwise, |this| might be corrupted. - // When the same key exists, it will be overwritten. - bool merge(const Value&); - - // Finds a value with |key|. It searches only children. - Value* findChild(const std::string& key); - const Value* findChild(const std::string& key) const; - // Sets a value, and returns the pointer to the created value. - // When the value having the same key exists, it will be overwritten. - Value* setChild(const std::string& key, const Value& v); - Value* setChild(const std::string& key, Value&& v); - bool eraseChild(const std::string& key); - - // ---------------------------------------------------------------------- - // For Array value - - template<typename T> typename call_traits<T>::return_type get(size_t index) const; - const Value* find(size_t index) const; - Value* find(size_t index); - Value* push(const Value& v); - Value* push(Value&& v); - - // ---------------------------------------------------------------------- - // Others - - // Writer. - static std::string spaces(int num); - static std::string escapeKey(const std::string& key); - - void write(std::ostream*, const std::string& keyPrefix = std::string(), int indent = -1) const; - void writeFormatted(std::ostream*, FormatFlag flags) const; - - friend std::ostream& operator<<(std::ostream&, const Value&); - -private: - static const char* typeToString(Type); - - template<typename T> void assureType() const; - Value* ensureValue(const std::string& key); - - template<typename T> struct ValueConverter; - - Type type_; - union { - void* null_; - bool bool_; - int64_t int_; - double double_; - std::string* string_; - Time* time_; - Array* array_; - Table* table_; - }; - - template<typename T> friend struct ValueConverter; -}; - -// parse() returns ParseResult. -struct ParseResult { - ParseResult(toml::Value v, std::string er) : - value(std::move(v)), - errorReason(std::move(er)) {} - - bool valid() const { return value.valid(); } - - toml::Value value; - std::string errorReason; -}; - -// Parses from std::istream. -ParseResult parse(std::istream&); - -// ---------------------------------------------------------------------- -// Declarations for Implementations -// You don't need to understand the below to use this library. - -#if defined(_WIN32) -// Windows does not have timegm but have _mkgmtime. -inline time_t timegm(std::tm* timeptr) -{ - return _mkgmtime(timeptr); -} -#endif -#if defined(_MSC_VER) -// Visual studio doesn't define gmtime_r, but mingw does -inline std::tm* gmtime_r(const time_t* timer, std::tm* result) -{ - gmtime_s(result, timer); - return result; -} -#endif - -namespace internal { - -enum class TokenType { - ERROR, - END_OF_FILE, - END_OF_LINE, - IDENT, - STRING, - MULTILINE_STRING, - BOOL, - INT, - DOUBLE, - TIME, - COMMA, - DOT, - EQUAL, - LBRACKET, - RBRACKET, - LBRACE, - RBRACE, -}; - -class Token { -public: - explicit Token(TokenType type) : type_(type) {} - Token(TokenType type, const std::string& v) : type_(type), str_value_(v) {} - Token(TokenType type, bool v) : type_(type), int_value_(v) {} - Token(TokenType type, std::int64_t v) : type_(type), int_value_(v) {} - Token(TokenType type, double v) : type_(type), double_value_(v) {} - Token(TokenType type, std::chrono::system_clock::time_point tp) : type_(type), time_value_(tp) {} - - TokenType type() const { return type_; } - const std::string& strValue() const { return str_value_; } - bool boolValue() const { return int_value_ != 0; } - std::int64_t intValue() const { return int_value_; } - double doubleValue() const { return double_value_; } - std::chrono::system_clock::time_point timeValue() const { return time_value_; } - -private: - TokenType type_; - std::string str_value_; - std::int64_t int_value_; - double double_value_; - std::chrono::system_clock::time_point time_value_; -}; - -class Lexer { -public: - explicit Lexer(std::istream& is) : is_(is), lineNo_(1) {} - - Token nextKeyToken(); - Token nextValueToken(); - - int lineNo() const { return lineNo_; } - -private: - bool current(char* c); - void next(); - bool consume(char c); - - Token nextToken(bool isValueToken); - - void skipUntilNewLine(); - - Token nextStringDoubleQuote(); - Token nextStringSingleQuote(); - - Token nextKey(); - Token nextValue(); - - Token parseAsTime(const std::string&); - - std::istream& is_; - int lineNo_; -}; - -class Parser { -public: - explicit Parser(std::istream& is) : lexer_(is), token_(TokenType::ERROR) { nextKey(); } - - // Parses. If failed, value should be invalid value. - // You can get the error by calling errorReason(). - Value parse(); - const std::string& errorReason(); - -private: - const Token& token() const { return token_; } - void nextKey() { token_ = lexer_.nextKeyToken(); } - void nextValue() { token_ = lexer_.nextValueToken(); } - - void skipForKey(); - void skipForValue(); - - bool consumeForKey(TokenType); - bool consumeForValue(TokenType); - bool consumeEOLorEOFForKey(); - - Value* parseGroupKey(Value* root); - - bool parseKeyValue(Value*); - bool parseKey(std::string*); - bool parseValue(Value*); - bool parseBool(Value*); - bool parseNumber(Value*); - bool parseArray(Value*); - bool parseInlineTable(Value*); - - void addError(const std::string& reason); - - Lexer lexer_; - Token token_; - std::string errorReason_; -}; - -} // namespace internal - -// ---------------------------------------------------------------------- -// Implementations - -inline ParseResult parse(std::istream& is) -{ - internal::Parser parser(is); - toml::Value v = parser.parse(); - - if (v.valid()) - return ParseResult(std::move(v), std::string()); - - return ParseResult(std::move(v), std::move(parser.errorReason())); -} - -inline std::string format(std::stringstream& ss) -{ - return ss.str(); -} - -template<typename T, typename... Args> -std::string format(std::stringstream& ss, T&& t, Args&&... args) -{ - ss << std::forward<T>(t); - return format(ss, std::forward<Args>(args)...); -} - -template<typename... Args> -#if defined(_MSC_VER) -__declspec(noreturn) -#else -[[noreturn]] -#endif -void failwith(Args&&... args) -{ - std::stringstream ss; - throw std::runtime_error(format(ss, std::forward<Args>(args)...)); -} - -namespace internal { - -inline std::string removeDelimiter(const std::string& s) -{ - std::string r; - for (char c : s) { - if (c == '_') - continue; - r += c; - } - return r; -} - -inline std::string unescape(const std::string& codepoint) -{ - std::uint32_t x; - std::uint8_t buf[8]; - std::stringstream ss(codepoint); - - ss >> std::hex >> x; - - if (x <= 0x7FUL) { - // 0xxxxxxx - buf[0] = 0x00 | ((x >> 0) & 0x7F); - buf[1] = '\0'; - } else if (x <= 0x7FFUL) { - // 110yyyyx 10xxxxxx - buf[0] = 0xC0 | ((x >> 6) & 0xDF); - buf[1] = 0x80 | ((x >> 0) & 0xBF); - buf[2] = '\0'; - } else if (x <= 0xFFFFUL) { - // 1110yyyy 10yxxxxx 10xxxxxx - buf[0] = 0xE0 | ((x >> 12) & 0xEF); - buf[1] = 0x80 | ((x >> 6) & 0xBF); - buf[2] = 0x80 | ((x >> 0) & 0xBF); - buf[3] = '\0'; - } else if (x <= 0x10FFFFUL) { - // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx - buf[0] = 0xF0 | ((x >> 18) & 0xF7); - buf[1] = 0x80 | ((x >> 12) & 0xBF); - buf[2] = 0x80 | ((x >> 6) & 0xBF); - buf[3] = 0x80 | ((x >> 0) & 0xBF); - buf[4] = '\0'; - } else { - buf[0] = '\0'; - } - - return reinterpret_cast<char*>(buf); -} - -// Returns true if |s| is integer. -// [+-]?\d+(_\d+)* -inline bool isInteger(const std::string& s) -{ - if (s.empty()) - return false; - - std::string::size_type p = 0; - if (s[p] == '+' || s[p] == '-') - ++p; - - while (p < s.size() && '0' <= s[p] && s[p] <= '9') { - ++p; - if (p < s.size() && s[p] == '_') { - ++p; - if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) - return false; - } - } - - return p == s.size(); -} - -// Returns true if |s| is double. -// [+-]? (\d+(_\d+)*)? (\.\d+(_\d+)*)? ([eE] [+-]? \d+(_\d+)*)? -// 1----------- 2------------- 3---------------------- -// 2 or (1 and 3) should exist. -inline bool isDouble(const std::string& s) -{ - if (s.empty()) - return false; - - std::string::size_type p = 0; - if (s[p] == '+' || s[p] == '-') - ++p; - - bool ok = false; - while (p < s.size() && '0' <= s[p] && s[p] <= '9') { - ++p; - ok = true; - - if (p < s.size() && s[p] == '_') { - ++p; - if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) - return false; - } - } - - if (p < s.size() && s[p] == '.') - ++p; - - while (p < s.size() && '0' <= s[p] && s[p] <= '9') { - ++p; - ok = true; - - if (p < s.size() && s[p] == '_') { - ++p; - if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) - return false; - } - } - - if (!ok) - return false; - - ok = false; - if (p < s.size() && (s[p] == 'e' || s[p] == 'E')) { - ++p; - if (p < s.size() && (s[p] == '+' || s[p] == '-')) - ++p; - while (p < s.size() && '0' <= s[p] && s[p] <= '9') { - ++p; - ok = true; - - if (p < s.size() && s[p] == '_') { - ++p; - if (!(p < s.size() && '0' <= s[p] && s[p] <= '9')) - return false; - } - } - if (!ok) - return false; - } - - return p == s.size(); -} - -// static -inline std::string escapeString(const std::string& s) -{ - std::stringstream ss; - for (size_t i = 0; i < s.size(); ++i) { - switch (s[i]) { - case '\n': ss << "\\n"; break; - case '\r': ss << "\\r"; break; - case '\t': ss << "\\t"; break; - case '\"': ss << "\\\""; break; - case '\'': ss << "\\\'"; break; - case '\\': ss << "\\\\"; break; - default: ss << s[i]; break; - } - } - - return ss.str(); -} - -} // namespace internal - -// ---------------------------------------------------------------------- -// Lexer - -namespace internal { - -inline bool Lexer::current(char* c) -{ - int x = is_.peek(); - if (x == EOF) - return false; - *c = static_cast<char>(x); - return true; -} - -inline void Lexer::next() -{ - int x = is_.get(); - if (x == '\n') - ++lineNo_; -} - -inline bool Lexer::consume(char c) -{ - char x; - if (!current(&x)) - return false; - if (x != c) - return false; - next(); - return true; -} - -inline void Lexer::skipUntilNewLine() -{ - char c; - while (current(&c)) { - if (c == '\n') - return; - next(); - } -} - -inline Token Lexer::nextStringDoubleQuote() -{ - if (!consume('"')) - return Token(TokenType::ERROR, std::string("string didn't start with '\"'")); - - std::string s; - char c; - bool multiline = false; - - if (current(&c) && c == '"') { - next(); - if (!current(&c) || c != '"') { - // OK. It's empty string. - return Token(TokenType::STRING, std::string()); - } - - next(); - // raw string literal started. - // Newline just after """ should be ignored. - while (current(&c) && (c == ' ' || c == '\t')) - next(); - if (current(&c) && c == '\n') - next(); - multiline = true; - } - - while (current(&c)) { - next(); - if (c == '\\') { - if (!current(&c)) - return Token(TokenType::ERROR, std::string("string has unknown escape sequence")); - next(); - switch (c) { - case 't': c = '\t'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 'u': - case 'U': { - int size = c == 'u' ? 4 : 8; - std::string codepoint; - for (int i = 0; i < size; ++i) { - if (current(&c) && (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))) { - codepoint += c; - next(); - } else { - return Token(TokenType::ERROR, std::string("string has unknown escape sequence")); - } - } - s += unescape(codepoint); - continue; - } - case '"': c = '"'; break; - case '\'': c = '\''; break; - case '\\': c = '\\'; break; - case '\n': - while (current(&c) && (c == ' ' || c == '\t' || c == '\r' || c == '\n')) { - next(); - } - continue; - default: - return Token(TokenType::ERROR, std::string("string has unknown escape sequence")); - } - } else if (c == '"') { - if (multiline) { - if (current(&c) && c == '"') { - next(); - if (current(&c) && c == '"') { - next(); - return Token(TokenType::MULTILINE_STRING, s); - } else { - s += '"'; - s += '"'; - continue; - } - } else { - s += '"'; - continue; - } - } else { - return Token(TokenType::STRING, s); - } - } - - s += c; - } - - return Token(TokenType::ERROR, std::string("string didn't end")); -} - -inline Token Lexer::nextStringSingleQuote() -{ - if (!consume('\'')) - return Token(TokenType::ERROR, std::string("string didn't start with '\''?")); - - std::string s; - char c; - - if (current(&c) && c == '\'') { - next(); - if (!current(&c) || c != '\'') { - // OK. It's empty string. - return Token(TokenType::STRING, std::string()); - } - next(); - // raw string literal started. - // Newline just after """ should be ignored. - if (current(&c) && c == '\n') - next(); - - while (current(&c)) { - if (c == '\'') { - next(); - if (current(&c) && c == '\'') { - next(); - if (current(&c) && c == '\'') { - next(); - return Token(TokenType::MULTILINE_STRING, s); - } else { - s += '\''; - s += '\''; - continue; - } - } else { - s += '\''; - continue; - } - } - - next(); - s += c; - continue; - } - - return Token(TokenType::ERROR, std::string("string didn't end with '\'\'\'' ?")); - } - - while (current(&c)) { - next(); - if (c == '\'') { - return Token(TokenType::STRING, s); - } - - s += c; - } - - return Token(TokenType::ERROR, std::string("string didn't end with '\''?")); -} - -inline Token Lexer::nextKey() -{ - std::string s; - char c; - while (current(&c) && (isalnum(c) || c == '_' || c == '-')) { - s += c; - next(); - } - - if (s.empty()) - return Token(TokenType::ERROR, std::string("Unknown key format")); - - return Token(TokenType::IDENT, s); -} - -inline Token Lexer::nextValue() -{ - std::string s; - char c; - - if (current(&c) && isalpha(c)) { - s += c; - next(); - while (current(&c) && isalpha(c)) { - s += c; - next(); - } - - if (s == "true") - return Token(TokenType::BOOL, true); - if (s == "false") - return Token(TokenType::BOOL, false); - return Token(TokenType::ERROR, std::string("Unknown ident: ") + s); - } - - while (current(&c) && (('0' <= c && c <= '9') || c == '.' || c == 'e' || c == 'E' || - c == 'T' || c == 'Z' || c == '_' || c == ':' || c == '-' || c == '+')) { - next(); - s += c; - } - - if (isInteger(s)) { - std::stringstream ss(removeDelimiter(s)); - std::int64_t x; - ss >> x; - return Token(TokenType::INT, x); - } - - if (isDouble(s)) { - std::stringstream ss(removeDelimiter(s)); - double d; - ss >> d; - return Token(TokenType::DOUBLE, d); - } - - return parseAsTime(s); -} - -inline Token Lexer::parseAsTime(const std::string& str) -{ - const char* s = str.c_str(); - - int n; - int YYYY, MM, DD; - if (sscanf(s, "%d-%d-%d%n", &YYYY, &MM, &DD, &n) != 3) - return Token(TokenType::ERROR, std::string("Invalid token")); - - if (s[n] == '\0') { - std::tm t; - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = DD; - t.tm_mon = MM - 1; - t.tm_year = YYYY - 1900; - auto tp = std::chrono::system_clock::from_time_t(timegm(&t)); - return Token(TokenType::TIME, tp); - } - - if (s[n] != 'T') - return Token(TokenType::ERROR, std::string("Invalid token")); - - s = s + n + 1; - - int hh, mm; - double ss; // double for fraction - if (sscanf(s, "%d:%d:%lf%n", &hh, &mm, &ss, &n) != 3) - return Token(TokenType::ERROR, std::string("Invalid token")); - - std::tm t; - t.tm_sec = static_cast<int>(ss); - t.tm_min = mm; - t.tm_hour = hh; - t.tm_mday = DD; - t.tm_mon = MM - 1; - t.tm_year = YYYY - 1900; - auto tp = std::chrono::system_clock::from_time_t(timegm(&t)); - ss -= static_cast<int>(ss); - // TODO(mayah): workaround GCC 4.9.3 on cygwin does not have std::round, but round(). - tp += std::chrono::microseconds(static_cast<std::int64_t>(round(ss * 1000000))); - - if (s[n] == '\0') - return Token(TokenType::TIME, tp); - - if (s[n] == 'Z' && s[n + 1] == '\0') - return Token(TokenType::TIME, tp); - - s = s + n; - // offset - // [+/-]%d:%d - char pn; - int oh, om; - if (sscanf(s, "%c%d:%d", &pn, &oh, &om) != 3) - return Token(TokenType::ERROR, std::string("Invalid token")); - - if (pn != '+' && pn != '-') - return Token(TokenType::ERROR, std::string("Invalid token")); - - if (pn == '+') { - tp -= std::chrono::hours(oh); - tp -= std::chrono::minutes(om); - } else { - tp += std::chrono::hours(oh); - tp += std::chrono::minutes(om); - } - - return Token(TokenType::TIME, tp); -} - -inline Token Lexer::nextKeyToken() -{ - return nextToken(false); -} - -inline Token Lexer::nextValueToken() -{ - return nextToken(true); -} - -inline Token Lexer::nextToken(bool isValueToken) -{ - char c; - while (current(&c)) { - if (c == ' ' || c == '\t' || c == '\r') { - next(); - continue; - } - - if (c == '#') { - skipUntilNewLine(); - continue; - } - - switch (c) { - case '\n': - next(); - return Token(TokenType::END_OF_LINE); - case '=': - next(); - return Token(TokenType::EQUAL); - case '{': - next(); - return Token(TokenType::LBRACE); - case '}': - next(); - return Token(TokenType::RBRACE); - case '[': - next(); - return Token(TokenType::LBRACKET); - case ']': - next(); - return Token(TokenType::RBRACKET); - case ',': - next(); - return Token(TokenType::COMMA); - case '.': - next(); - return Token(TokenType::DOT); - case '\"': - return nextStringDoubleQuote(); - case '\'': - return nextStringSingleQuote(); - default: - if (isValueToken) { - return nextValue(); - } else { - return nextKey(); - } - } - } - - return Token(TokenType::END_OF_FILE); -} - -} // namespace internal - -// ---------------------------------------------------------------------- - -// static -inline const char* Value::typeToString(Value::Type type) -{ - switch (type) { - case NULL_TYPE: return "null"; - case BOOL_TYPE: return "bool"; - case INT_TYPE: return "int"; - case DOUBLE_TYPE: return "double"; - case STRING_TYPE: return "string"; - case TIME_TYPE: return "time"; - case ARRAY_TYPE: return "array"; - case TABLE_TYPE: return "table"; - default: return "unknown"; - } -} - -inline Value::Value(const Value& v) : - type_(v.type_) -{ - switch (v.type_) { - case NULL_TYPE: null_ = v.null_; break; - case BOOL_TYPE: bool_ = v.bool_; break; - case INT_TYPE: int_ = v.int_; break; - case DOUBLE_TYPE: double_ = v.double_; break; - case STRING_TYPE: string_ = new std::string(*v.string_); break; - case TIME_TYPE: time_ = new Time(*v.time_); break; - case ARRAY_TYPE: array_ = new Array(*v.array_); break; - case TABLE_TYPE: table_ = new Table(*v.table_); break; - default: - assert(false); - type_ = NULL_TYPE; - null_ = nullptr; - } -} - -inline Value::Value(Value&& v) noexcept : - type_(v.type_) -{ - switch (v.type_) { - case NULL_TYPE: null_ = v.null_; break; - case BOOL_TYPE: bool_ = v.bool_; break; - case INT_TYPE: int_ = v.int_; break; - case DOUBLE_TYPE: double_ = v.double_; break; - case STRING_TYPE: string_ = v.string_; break; - case TIME_TYPE: time_ = v.time_; break; - case ARRAY_TYPE: array_ = v.array_; break; - case TABLE_TYPE: table_ = v.table_; break; - default: - assert(false); - type_ = NULL_TYPE; - null_ = nullptr; - } - - v.type_ = NULL_TYPE; - v.null_ = nullptr; -} - -inline Value& Value::operator=(const Value& v) -{ - if (this == &v) - return *this; - - this->~Value(); - - type_ = v.type_; - switch (v.type_) { - case NULL_TYPE: null_ = v.null_; break; - case BOOL_TYPE: bool_ = v.bool_; break; - case INT_TYPE: int_ = v.int_; break; - case DOUBLE_TYPE: double_ = v.double_; break; - case STRING_TYPE: string_ = new std::string(*v.string_); break; - case TIME_TYPE: time_ = new Time(*v.time_); break; - case ARRAY_TYPE: array_ = new Array(*v.array_); break; - case TABLE_TYPE: table_ = new Table(*v.table_); break; - default: - assert(false); - type_ = NULL_TYPE; - null_ = nullptr; - } - - return *this; -} - -inline Value& Value::operator=(Value&& v) noexcept -{ - if (this == &v) - return *this; - - this->~Value(); - - type_ = v.type_; - switch (v.type_) { - case NULL_TYPE: null_ = v.null_; break; - case BOOL_TYPE: bool_ = v.bool_; break; - case INT_TYPE: int_ = v.int_; break; - case DOUBLE_TYPE: double_ = v.double_; break; - case STRING_TYPE: string_ = v.string_; break; - case TIME_TYPE: time_ = v.time_; break; - case ARRAY_TYPE: array_ = v.array_; break; - case TABLE_TYPE: table_ = v.table_; break; - default: - assert(false); - type_ = NULL_TYPE; - null_ = nullptr; - } - - v.type_ = NULL_TYPE; - v.null_ = nullptr; - return *this; -} - -inline Value::~Value() -{ - switch (type_) { - case STRING_TYPE: - delete string_; - break; - case TIME_TYPE: - delete time_; - break; - case ARRAY_TYPE: - delete array_; - break; - case TABLE_TYPE: - delete table_; - break; - default: - break; - } -} - -inline size_t Value::size() const -{ - switch (type_) { - case NULL_TYPE: - return 0; - case ARRAY_TYPE: - return array_->size(); - case TABLE_TYPE: - return table_->size(); - default: - return 1; - } -} - -inline bool Value::empty() const -{ - return size() == 0; -} - -template<> struct Value::ValueConverter<bool> -{ - bool is(const Value& v) { return v.type() == Value::BOOL_TYPE; } - bool to(const Value& v) { v.assureType<bool>(); return v.bool_; } - -}; -template<> struct Value::ValueConverter<int64_t> -{ - bool is(const Value& v) { return v.type() == Value::INT_TYPE; } - int64_t to(const Value& v) { v.assureType<int64_t>(); return v.int_; } -}; -template<> struct Value::ValueConverter<int> -{ - bool is(const Value& v) { return v.type() == Value::INT_TYPE; } - int to(const Value& v) { v.assureType<int>(); return static_cast<int>(v.int_); } -}; -template<> struct Value::ValueConverter<double> -{ - bool is(const Value& v) { return v.type() == Value::DOUBLE_TYPE; } - double to(const Value& v) { v.assureType<double>(); return v.double_; } -}; -template<> struct Value::ValueConverter<std::string> -{ - bool is(const Value& v) { return v.type() == Value::STRING_TYPE; } - const std::string& to(const Value& v) { v.assureType<std::string>(); return *v.string_; } -}; -template<> struct Value::ValueConverter<Time> -{ - bool is(const Value& v) { return v.type() == Value::TIME_TYPE; } - const Time& to(const Value& v) { v.assureType<Time>(); return *v.time_; } -}; -template<> struct Value::ValueConverter<Array> -{ - bool is(const Value& v) { return v.type() == Value::ARRAY_TYPE; } - const Array& to(const Value& v) { v.assureType<Array>(); return *v.array_; } -}; -template<> struct Value::ValueConverter<Table> -{ - bool is(const Value& v) { return v.type() == Value::TABLE_TYPE; } - const Table& to(const Value& v) { v.assureType<Table>(); return *v.table_; } -}; - -template<typename T> -struct Value::ValueConverter<std::vector<T>> -{ - bool is(const Value& v) - { - if (v.type() != Value::ARRAY_TYPE) - return false; - const Array& array = v.as<Array>(); - if (array.empty()) - return true; - return array.front().is<T>(); - } - - std::vector<T> to(const Value& v) - { - const Array& array = v.as<Array>(); - if (array.empty()) - return std::vector<T>(); - array.front().assureType<T>(); - - std::vector<T> result; - for (const auto& element : array) { - result.push_back(element.as<T>()); - } - - return result; - } -}; - -namespace internal { -template<typename T> inline const char* type_name(); -template<> inline const char* type_name<bool>() { return "bool"; } -template<> inline const char* type_name<int>() { return "int"; } -template<> inline const char* type_name<int64_t>() { return "int64_t"; } -template<> inline const char* type_name<double>() { return "double"; } -template<> inline const char* type_name<std::string>() { return "string"; } -template<> inline const char* type_name<toml::Time>() { return "time"; } -template<> inline const char* type_name<toml::Array>() { return "array"; } -template<> inline const char* type_name<toml::Table>() { return "table"; } -} // namespace internal - -template<typename T> -inline void Value::assureType() const -{ - if (!is<T>()) - failwith("type error: this value is ", typeToString(type_), " but ", internal::type_name<T>(), " was requested"); -} - -template<typename T> -inline bool Value::is() const -{ - return ValueConverter<T>().is(*this); -} - -template<typename T> -inline typename call_traits<T>::return_type Value::as() const -{ - return ValueConverter<T>().to(*this); -} - -inline bool Value::isNumber() const -{ - return is<int>() || is<double>(); -} - -inline double Value::asNumber() const -{ - if (is<int>()) - return as<int>(); - if (is<double>()) - return as<double>(); - - failwith("type error: this value is ", typeToString(type_), " but number is requested"); -} - -inline std::time_t Value::as_time_t() const -{ - return std::chrono::system_clock::to_time_t(as<Time>()); -} - -inline std::string Value::spaces(int num) -{ - if (num <= 0) - return std::string(); - - return std::string(num, ' '); -} - -inline std::string Value::escapeKey(const std::string& key) -{ - auto position = std::find_if(key.begin(), key.end(), [](char c) -> bool { - if (std::isalnum(c) || c == '_' || c == '-') - return false; - return true; - }); - - if (position != key.end()) { - std::string escaped = "\""; - for (const char& c : key) { - if (c == '\\' || c == '"') - escaped += '\\'; - escaped += c; - } - escaped += "\""; - - return escaped; - } - - return key; -} - -inline void Value::write(std::ostream* os, const std::string& keyPrefix, int indent) const -{ - switch (type_) { - case NULL_TYPE: - failwith("null type value is not a valid value"); - break; - case BOOL_TYPE: - (*os) << (bool_ ? "true" : "false"); - break; - case INT_TYPE: - (*os) << int_; - break; - case DOUBLE_TYPE: { - (*os) << std::fixed << std::showpoint << double_; - break; - } - case STRING_TYPE: - (*os) << '"' << internal::escapeString(*string_) << '"'; - break; - case TIME_TYPE: { - time_t tt = std::chrono::system_clock::to_time_t(*time_); - std::tm t; - gmtime_r(&tt, &t); - char buf[256]; - sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); - (*os) << buf; - break; - } - case ARRAY_TYPE: - (*os) << '['; - for (size_t i = 0; i < array_->size(); ++i) { - if (i) - (*os) << ", "; - (*array_)[i].write(os, keyPrefix, -1); - } - (*os) << ']'; - break; - case TABLE_TYPE: - for (const auto& kv : *table_) { - if (kv.second.is<Table>()) - continue; - if (kv.second.is<Array>() && kv.second.size() > 0 && kv.second.find(0)->is<Table>()) - continue; - (*os) << spaces(indent) << escapeKey(kv.first) << " = "; - kv.second.write(os, keyPrefix, indent >= 0 ? indent + 1 : indent); - (*os) << '\n'; - } - for (const auto& kv : *table_) { - if (kv.second.is<Table>()) { - std::string key(keyPrefix); - if (!keyPrefix.empty()) - key += "."; - key += escapeKey(kv.first); - (*os) << "\n" << spaces(indent) << "[" << key << "]\n"; - kv.second.write(os, key, indent >= 0 ? indent + 1 : indent); - } - if (kv.second.is<Array>() && kv.second.size() > 0 && kv.second.find(0)->is<Table>()) { - std::string key(keyPrefix); - if (!keyPrefix.empty()) - key += "."; - key += escapeKey(kv.first); - for (const auto& v : kv.second.as<Array>()) { - (*os) << "\n" << spaces(indent) << "[[" << key << "]]\n"; - v.write(os, key, indent >= 0 ? indent + 1 : indent); - } - } - } - break; - default: - failwith("writing unknown type"); - break; - } -} - -inline void Value::writeFormatted(std::ostream* os, FormatFlag flags) const -{ - int indent = flags & FORMAT_INDENT ? 0 : -1; - - write(os, std::string(), indent); -} - -// static -inline FormatFlag operator|(FormatFlag lhs, FormatFlag rhs) -{ - return static_cast<FormatFlag>(static_cast<int>(lhs) | static_cast<int>(rhs)); -} - -// static -inline std::ostream& operator<<(std::ostream& os, const toml::Value& v) -{ - v.write(&os); - return os; -} - -// static -inline bool operator==(const Value& lhs, const Value& rhs) -{ - if (lhs.type() != rhs.type()) - return false; - - switch (lhs.type()) { - case Value::Type::NULL_TYPE: - return true; - case Value::Type::BOOL_TYPE: - return lhs.bool_ == rhs.bool_; - case Value::Type::INT_TYPE: - return lhs.int_ == rhs.int_; - case Value::Type::DOUBLE_TYPE: - return lhs.double_ == rhs.double_; - case Value::Type::STRING_TYPE: - return *lhs.string_ == *rhs.string_; - case Value::Type::TIME_TYPE: - return *lhs.time_ == *rhs.time_; - case Value::Type::ARRAY_TYPE: - return *lhs.array_ == *rhs.array_; - case Value::Type::TABLE_TYPE: - return *lhs.table_ == *rhs.table_; - default: - failwith("unknown type"); - } -} - -template<typename T> -inline typename call_traits<T>::return_type Value::get(const std::string& key) const -{ - if (!is<Table>()) - failwith("type must be table to do get(key)."); - - const Value* obj = find(key); - if (!obj) - failwith("key ", key, " was not found."); - - return obj->as<T>(); -} - -inline const Value* Value::find(const std::string& key) const -{ - if (!is<Table>()) - return nullptr; - - std::istringstream ss(key); - internal::Lexer lexer(ss); - - const Value* current = this; - while (true) { - internal::Token t = lexer.nextKeyToken(); - if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING)) - return nullptr; - - std::string part = t.strValue(); - t = lexer.nextKeyToken(); - if (t.type() == internal::TokenType::DOT) { - current = current->findChild(part); - if (!current || !current->is<Table>()) - return nullptr; - } else if (t.type() == internal::TokenType::END_OF_FILE) { - return current->findChild(part); - } else { - return nullptr; - } - } -} - -inline Value* Value::find(const std::string& key) -{ - return const_cast<Value*>(const_cast<const Value*>(this)->find(key)); -} - -inline bool Value::merge(const toml::Value& v) -{ - if (this == &v) - return true; - if (!is<Table>() || !v.is<Table>()) - return false; - - for (const auto& kv : *v.table_) { - if (Value* tmp = find(kv.first)) { - // If both are table, we merge them. - if (tmp->is<Table>() && kv.second.is<Table>()) { - if (!tmp->merge(kv.second)) - return false; - } else { - setChild(kv.first, kv.second); - } - } else { - setChild(kv.first, kv.second); - } - } - - return true; -} - -inline Value* Value::set(const std::string& key, const Value& v) -{ - Value* result = ensureValue(key); - *result = v; - return result; -} - -inline Value* Value::setChild(const std::string& key, const Value& v) -{ - if (!valid()) - *this = Value((Table())); - - if (!is<Table>()) - failwith("type must be table to do set(key, v)."); - - (*table_)[key] = v; - return &(*table_)[key]; -} - -inline Value* Value::setChild(const std::string& key, Value&& v) -{ - if (!valid()) - *this = Value((Table())); - - if (!is<Table>()) - failwith("type must be table to do set(key, v)."); - - (*table_)[key] = std::move(v); - return &(*table_)[key]; -} - -inline bool Value::erase(const std::string& key) -{ - if (!is<Table>()) - return false; - - std::istringstream ss(key); - internal::Lexer lexer(ss); - - Value* current = this; - while (true) { - internal::Token t = lexer.nextKeyToken(); - if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING)) - return false; - - std::string part = t.strValue(); - t = lexer.nextKeyToken(); - if (t.type() == internal::TokenType::DOT) { - current = current->findChild(part); - if (!current || !current->is<Table>()) - return false; - } else if (t.type() == internal::TokenType::END_OF_FILE) { - return current->eraseChild(part); - } else { - return false; - } - } -} - -inline bool Value::eraseChild(const std::string& key) -{ - if (!is<Table>()) - failwith("type must be table to do erase(key)."); - - return table_->erase(key) > 0; -} - -inline Value& Value::operator[](const std::string& key) -{ - if (!valid()) - *this = Value((Table())); - - if (Value* v = findChild(key)) - return *v; - - return *setChild(key, Value()); -} - -template<typename T> -inline typename call_traits<T>::return_type Value::get(size_t index) const -{ - if (!is<Array>()) - failwith("type must be array to do get(index)."); - - if (array_->size() <= index) - failwith("index out of bound"); - - return (*array_)[index].as<T>(); -} - -inline const Value* Value::find(size_t index) const -{ - if (!is<Array>()) - return nullptr; - if (index < array_->size()) - return &(*array_)[index]; - return nullptr; -} - -inline Value* Value::find(size_t index) -{ - return const_cast<Value*>(const_cast<const Value*>(this)->find(index)); -} - -inline Value* Value::push(const Value& v) -{ - if (!valid()) - *this = Value((Array())); - else if (!is<Array>()) - failwith("type must be array to do push(Value)."); - - array_->push_back(v); - return &array_->back(); -} - -inline Value* Value::push(Value&& v) -{ - if (!valid()) - *this = Value((Array())); - else if (!is<Array>()) - failwith("type must be array to do push(Value)."); - - array_->push_back(std::move(v)); - return &array_->back(); -} - -inline Value* Value::ensureValue(const std::string& key) -{ - if (!valid()) - *this = Value((Table())); - if (!is<Table>()) { - failwith("encountered non table value"); - } - - std::istringstream ss(key); - internal::Lexer lexer(ss); - - Value* current = this; - while (true) { - internal::Token t = lexer.nextKeyToken(); - if (!(t.type() == internal::TokenType::IDENT || t.type() == internal::TokenType::STRING)) { - failwith("invalid key"); - } - - std::string part = t.strValue(); - t = lexer.nextKeyToken(); - if (t.type() == internal::TokenType::DOT) { - if (Value* candidate = current->findChild(part)) { - if (!candidate->is<Table>()) - failwith("encountered non table value"); - - current = candidate; - } else { - current = current->setChild(part, Table()); - } - } else if (t.type() == internal::TokenType::END_OF_FILE) { - if (Value* v = current->findChild(part)) - return v; - return current->setChild(part, Value()); - } else { - failwith("invalid key"); - } - } -} - -inline Value* Value::findChild(const std::string& key) -{ - assert(is<Table>()); - - auto it = table_->find(key); - if (it == table_->end()) - return nullptr; - - return &it->second; -} - -inline const Value* Value::findChild(const std::string& key) const -{ - assert(is<Table>()); - - auto it = table_->find(key); - if (it == table_->end()) - return nullptr; - - return &it->second; -} - -// ---------------------------------------------------------------------- - -namespace internal { - -inline void Parser::skipForKey() -{ - while (token().type() == TokenType::END_OF_LINE) - nextKey(); -} - -inline void Parser::skipForValue() -{ - while (token().type() == TokenType::END_OF_LINE) - nextValue(); -} - -inline bool Parser::consumeForKey(TokenType type) -{ - if (token().type() == type) { - nextKey(); - return true; - } - - return false; -} - -inline bool Parser::consumeForValue(TokenType type) -{ - if (token().type() == type) { - nextValue(); - return true; - } - - return false; -} - -inline bool Parser::consumeEOLorEOFForKey() -{ - if (token().type() == TokenType::END_OF_LINE || token().type() == TokenType::END_OF_FILE) { - nextKey(); - return true; - } - - return false; -} - -inline void Parser::addError(const std::string& reason) -{ - if (!errorReason_.empty()) - return; - - std::stringstream ss; - ss << "Error: line " << lexer_.lineNo() << ": " << reason; - errorReason_ = ss.str(); -} - -inline const std::string& Parser::errorReason() -{ - return errorReason_; -} - -inline Value Parser::parse() -{ - Value root((Table())); - Value* currentValue = &root; - - while (true) { - skipForKey(); - if (token().type() == TokenType::END_OF_FILE) - break; - if (token().type() == TokenType::LBRACKET) { - currentValue = parseGroupKey(&root); - if (!currentValue) { - addError("error when parsing group key"); - return Value(); - } - continue; - } - - if (!parseKeyValue(currentValue)) { - addError("error when parsing key Value"); - return Value(); - } - } - return root; -} - -inline Value* Parser::parseGroupKey(Value* root) -{ - if (!consumeForKey(TokenType::LBRACKET)) - return nullptr; - - bool isArray = false; - if (token().type() == TokenType::LBRACKET) { - nextKey(); - isArray = true; - } - - Value* currentValue = root; - while (true) { - if (token().type() != TokenType::IDENT && token().type() != TokenType::STRING) - return nullptr; - - std::string key = token().strValue(); - nextKey(); - - if (token().type() == TokenType::DOT) { - nextKey(); - if (Value* candidate = currentValue->findChild(key)) { - if (candidate->is<Array>() && candidate->size() > 0) - candidate = candidate->find(candidate->size() - 1); - if (!candidate->is<Table>()) - return nullptr; - currentValue = candidate; - } else { - currentValue = currentValue->setChild(key, Table()); - } - continue; - } - - if (token().type() == TokenType::RBRACKET) { - nextKey(); - if (Value* candidate = currentValue->findChild(key)) { - if (isArray) { - if (!candidate->is<Array>()) - return nullptr; - currentValue = candidate->push(Table()); - } else { - if (candidate->is<Array>() && candidate->size() > 0) - candidate = candidate->find(candidate->size() - 1); - if (!candidate->is<Table>()) - return nullptr; - currentValue = candidate; - } - } else { - if (isArray) { - currentValue = currentValue->setChild(key, Array()); - currentValue = currentValue->push(Table()); - } else { - currentValue = currentValue->setChild(key, Table()); - } - } - break; - } - - return nullptr; - } - - if (isArray) { - if (!consumeForKey(TokenType::RBRACKET)) - return nullptr; - } - - if (!consumeEOLorEOFForKey()) - return nullptr; - - return currentValue; -} - -inline bool Parser::parseKeyValue(Value* current) -{ - std::string key; - if (!parseKey(&key)) { - addError("parse key failed"); - return false; - } - if (!consumeForValue(TokenType::EQUAL)) { - addError("no equal?"); - return false; - } - - Value v; - if (!parseValue(&v)) - return false; - if (!consumeEOLorEOFForKey()) - return false; - - if (current->has(key)) { - addError("Multiple same key: " + key); - return false; - } - - current->setChild(key, std::move(v)); - return true; -} - -inline bool Parser::parseKey(std::string* key) -{ - key->clear(); - - if (token().type() == TokenType::IDENT || token().type() == TokenType::STRING) { - *key = token().strValue(); - nextValue(); - return true; - } - - return false; -} - -inline bool Parser::parseValue(Value* v) -{ - switch (token().type()) { - case TokenType::STRING: - case TokenType::MULTILINE_STRING: - *v = token().strValue(); - nextValue(); - return true; - case TokenType::LBRACKET: - return parseArray(v); - case TokenType::LBRACE: - return parseInlineTable(v); - case TokenType::BOOL: - *v = token().boolValue(); - nextValue(); - return true; - case TokenType::INT: - *v = token().intValue(); - nextValue(); - return true; - case TokenType::DOUBLE: - *v = token().doubleValue(); - nextValue(); - return true; - case TokenType::TIME: - *v = token().timeValue(); - nextValue(); - return true; - case TokenType::ERROR: - addError(token().strValue()); - return false; - default: - addError("unexpected token"); - return false; - } -} - -inline bool Parser::parseBool(Value* v) -{ - if (token().strValue() == "true") { - nextValue(); - *v = true; - return true; - } - - if (token().strValue() == "false") { - nextValue(); - *v = false; - return true; - } - - return false; -} - -inline bool Parser::parseArray(Value* v) -{ - if (!consumeForValue(TokenType::LBRACKET)) - return false; - - Array a; - while (true) { - skipForValue(); - - if (token().type() == TokenType::RBRACKET) - break; - - skipForValue(); - Value x; - if (!parseValue(&x)) - return false; - - if (!a.empty()) { - if (a.front().type() != x.type()) { - addError("type check failed"); - return false; - } - } - - a.push_back(std::move(x)); - skipForValue(); - if (token().type() == TokenType::RBRACKET) - break; - if (token().type() == TokenType::COMMA) - nextValue(); - } - - if (!consumeForValue(TokenType::RBRACKET)) - return false; - *v = std::move(a); - return true; -} - -inline bool Parser::parseInlineTable(Value* value) -{ - // For inline table, next is KEY, so use consumeForKey here. - if (!consumeForKey(TokenType::LBRACE)) - return false; - - Value t((Table())); - bool first = true; - while (true) { - if (token().type() == TokenType::RBRACE) { - break; - } - - if (!first) { - if (token().type() != TokenType::COMMA) { - addError("inline table didn't have ',' for delimiter?"); - return false; - } - nextKey(); - } - first = false; - - std::string key; - if (!parseKey(&key)) - return false; - if (!consumeForValue(TokenType::EQUAL)) - return false; - Value v; - if (!parseValue(&v)) - return false; - - if (t.has(key)) { - addError("inline table has multiple same keys: key=" + key); - return false; - } - - t.set(key, v); - } - - if (!consumeForValue(TokenType::RBRACE)) - return false; - *value = std::move(t); - return true; -} - -} // namespace internal -} // namespace toml - -#endif // TINYTOML_H_ diff --git a/src/browser.cpp b/src/browser.cpp index 14fee3e..60b8810 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -21,16 +21,14 @@ #include "browser.h" #include "mainwindow.h" #include <QtWebEngine> -#include <QMessageBox> #include <QDir> -#include <QPluginLoader> -#include "interfaces.h" Browser::Browser(int &argc, char *argv[]) : SingleApplication(argc, argv) { setApplicationName("smolbote"); + setWindowIcon(QIcon(":/icon.svg")); #ifdef GIT_VERSION setApplicationVersion(GIT_VERSION); #else @@ -40,95 +38,58 @@ Browser::Browser(int &argc, char *argv[]) : Browser::~Browser() { - qDeleteAll(m_windows); - m_windows.clear(); +// qDeleteAll(m_windows); +// m_windows.clear(); } -QString Browser::applicationLongVersion() const +void Browser::setConfiguration(std::shared_ptr<Configuration> &config) { -#ifdef GIT_DESCRIBE - return QString(GIT_DESCRIBE); -#else - return applicationVersion(); -#endif + m_config = config; + +// m_bookmarksManager = std::make_shared<BookmarksWidget>(QString::fromStdString(m_config->value<std::string>("bookmarks.path").value())); +// m_downloadManager = std::make_shared<DownloadsWidget>(QString::fromStdString(m_config->value<std::string>("downloads.path").value())); } -void Browser::loadPlugins() +void Browser::loadProfiles() { - // Loading plugins - qDebug(">> Looking for plugins..."); + Q_ASSERT(m_config); - // Look for plugins in "../lib/smolbote/plugins" - QDir dir = QDir::current(); - dir.cd("../lib/smolbote/plugins"); + const QString &path = QString::fromStdString(m_config->value<std::string>("profile.path").value()); - // Load all plugins - const QStringList files = dir.entryList(QDir::Files | QDir::Readable); - for(const QString &filename : files) { - qDebug("Loading %s", qUtf8Printable(filename)); +#ifdef QT_DEBUG + qDebug(">> Looking for profiles... [%s]", qUtf8Printable(path)); +#endif - QPluginLoader loader(dir.absoluteFilePath(filename)); - QObject *plugin = loader.instance(); - if(plugin) { - PluginInterface *p = qobject_cast<PluginInterface *>(plugin); - if(p) { - qDebug("Successfully loaded plugin [name = %s]", qUtf8Printable(p->name())); - m_plugin = plugin; - } - } else { - qDebug("Plugin load failed"); - } - } + // Build a profile list from the folders in the profile.path + QDir profileDir(path); + const QStringList profileList = profileDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - qDebug("<< Plugins end..."); -} - -void Browser::loadProfiles() -{ - qDebug(">> Looking for profiles..."); - profile(""); - QDir dir(settings()->value("browser.profile.path").toString()); - const QStringList profileList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for(const QString &name : profileList) { - qDebug("- Adding profile %s", qUtf8Printable(name)); - profile(name); + m_profiles.insert(name, new WebEngineProfile(name, profileDir.absoluteFilePath(name), this)); } - qDebug("<< Profiles end..."); - //connect(this, SIGNAL(messageAvailable(QStringList)), this, SLOT(addWindow(QStringList))); - connect(this, &Browser::messageAvailable, this, [&](const QHash<QString, QVariant> params) { + // Also add the Off-the-record profile + m_profiles.insert("", new WebEngineProfile(this)); - //qDebug("Creating new window for [%s]", qUtf8Printable(params["urls"].toString())); + // set default profile + m_defaultProfile = m_profiles[QString::fromStdString(m_config->value<std::string>("browser.profile").value())]; - createWindow(params); - }); -} - -Browser *Browser::instance() -{ - return static_cast<Browser *>(QCoreApplication::instance()); -} - -Settings *Browser::settings() -{ - Q_ASSERT(m_settings); - return m_settings.get(); +#ifdef QT_DEBUG + qDebug("<< Profiles end..."); +#endif } -BookmarksWidget *Browser::bookmarks() +/* +std::shared_ptr<BookmarksWidget> &Browser::bookmarks() { - if(!m_bookmarksManager) { - m_bookmarksManager = QSharedPointer<BookmarksWidget>(new BookmarksWidget(settings()->value("bookmarks.path").toString()), &QObject::deleteLater); - } - return m_bookmarksManager.data(); + Q_ASSERT(m_bookmarksManager); + return m_bookmarksManager; } -DownloadsWidget *Browser::downloads() +std::shared_ptr<DownloadsWidget> &Browser::downloads() { - if(!m_downloadManager) { - m_downloadManager = QSharedPointer<DownloadsWidget>(new DownloadsWidget(settings()->value("downloads.path").toString()), &QObject::deleteLater); - } - return m_downloadManager.data(); + Q_ASSERT(m_downloadManager); + return m_downloadManager; } BlockerManager *Browser::blocklists() @@ -139,52 +100,6 @@ BlockerManager *Browser::blocklists() return m_blocklistManager; } -void Browser::loadSettings(const QString &path) -{ - QString configLocation, defaultsLocation; - - // set custom config path if any - if(!path.isEmpty()) { - configLocation = path; - - } else { - // no custom config has been set - // check if config file exists for this user - QString cpath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/poi.conf"; - if(QFile::exists(cpath)) { - configLocation = cpath; - } - // else there is no user overrides - } - - // set defaults location - // check system-specific locations -#ifdef Q_OS_LINUX - if(QFile::exists("/usr/share/smolbote/poi.conf")) { - defaultsLocation = "/usr/share/smolbote/poi.conf"; - } else if(QFile::exists("/usr/local/share/smolbote/poi.conf")) { - defaultsLocation = "/usr/local/share/smolbote/poi.conf"; - } -#endif - - // if no default config is found, then use the built-in one - if(defaultsLocation.isEmpty()) { - defaultsLocation = ":/poi.toml"; - } - - m_settings = std::unique_ptr<Settings>(new Settings(configLocation, defaultsLocation)); - -#ifdef QT_DEBUG - if(m_settings->isEmpty()) { - // There are no keys in the settings - QMessageBox::information(0, - tr("Configuration is empty"), - tr("The configuration file <i>%1</i> is empty.<br>" - "Using default values from <i>%2</i>.").arg(configLocation, defaultsLocation)); - } -#endif -} - MainWindow *Browser::activeWindow() { if(m_windows.empty()) { @@ -197,73 +112,30 @@ MainWindow *Browser::activeWindow() } } return m_windows.first().data(); -} +}*/ -MainWindow *Browser::createWindow(const QHash<QString, QVariant> options) +MainWindow *Browser::createWindow() { - Q_ASSERT(options.contains("urls")); + // the window will delete itself when it closes, so we don't need to delete it + MainWindow *window = new MainWindow(m_config); + window->setProfile(m_defaultProfile); - MainWindow *w = new MainWindow(); + m_windows.append(window); - m_windows.append(QPointer<MainWindow>(w)); - connect(w, &MainWindow::destroyed, this, [&]() { - clean(); + // has to be window.get(), but can't be *window + connect(window, &MainWindow::destroyed, this, [this, window]() { + m_windows.removeOne(window); }); + window->show(); - QString profileName; - if(options.contains("profile")) { - profileName = options.value("profile").toString(); - } - const QStringList urls = options.value("urls").toStringList(); - if(urls.isEmpty()) { - w->newTab(profile(profileName)->homepage()); - } else { - for(const QString url : urls) { - w->newTab(QUrl::fromUserInput(url)); - } - } - - w->show(); - - return w; -} - -void Browser::clean() -{ - for(int i = m_windows.count() - 1; i >= 0; i--) { - if(m_windows[i].isNull()) { - m_windows.removeAt(i); -#ifdef QT_DEBUG - qDebug("Removed deleted window from window list"); -#endif - } - } + return window; } WebEngineProfile* Browser::profile(const QString name) { - if(!m_profiles.contains(name)) { - if(name.isEmpty()) { - // Create off-the-record profile - m_profiles.insert(name, new WebEngineProfile(this)); - } else { - // Create regular profile - m_profiles.insert(name, new WebEngineProfile(name, this)); - } - - if(!m_urlRequestInterceptor) { - m_urlRequestInterceptor = new UrlRequestInterceptor(this); - m_urlRequestInterceptor->setSubscription(blocklists()); - } - - m_profiles[name]->setRequestInterceptor(m_urlRequestInterceptor); - - connect(m_profiles[name], SIGNAL(downloadRequested(QWebEngineDownloadItem*)), downloads(), SLOT(addDownload(QWebEngineDownloadItem*))); - } - return m_profiles[name]; } - +/* QStringList Browser::profiles() { QStringList l; @@ -273,8 +145,4 @@ QStringList Browser::profiles() } return l; } - -QObject *Browser::plugin(const QString name) -{ - return m_plugin; -} +*/ diff --git a/src/browser.h b/src/browser.h index dedb933..ee9e917 100644 --- a/src/browser.h +++ b/src/browser.h @@ -22,21 +22,16 @@ #define BROWSER_H #include "singleapplication.h" -#include <bookmarks/bookmarkswidget.h> -#include <downloads/downloadswidget.h> -#include "settings.h" +//#include <bookmarks/bookmarkswidget.h> +//#include <downloads/downloadswidget.h> #include "webengine/webengineprofile.h" -#include "filter/blockermanager.h" -#include "webengine/urlinterceptor.h" +//#include "filter/blockermanager.h" +//#include "webengine/urlinterceptor.h" +#include <QVector> #include <memory> -#include <QSharedPointer> - -#ifdef browser -#undef browser -#endif -#define browser Browser::instance() +#include "settings/configuration.h" class MainWindow; class Browser : public SingleApplication @@ -47,45 +42,35 @@ public: explicit Browser(int &argc, char *argv[]); ~Browser(); - QString applicationLongVersion() const; + void setConfiguration(std::shared_ptr<Configuration> &config); - void loadSettings(const QString &path); - void loadPlugins(); void loadProfiles(); - - static Browser *instance(); - - Settings *settings(); - BookmarksWidget *bookmarks(); - DownloadsWidget *downloads(); - BlockerManager *blocklists(); - - MainWindow *activeWindow(); - WebEngineProfile *profile(const QString name); - QStringList profiles(); + // QStringList profiles(); + +// std::shared_ptr<BookmarksWidget>& bookmarks(); +// std::shared_ptr<DownloadsWidget>& downloads(); +// BlockerManager *blocklists(); - QObject *plugin(const QString name); - QStringList plugins(); + //MainWindow *activeWindow(); public slots: - MainWindow* createWindow(const QHash<QString, QVariant> options); - void clean(); + MainWindow* createWindow(); private: Q_DISABLE_COPY(Browser) - std::unique_ptr<Settings> m_settings; + std::shared_ptr<Configuration> m_config; - QVector<QPointer<MainWindow>> m_windows; + QVector<MainWindow *> m_windows; +// QVector<QPointer<MainWindow>> m_windows; QHash<QString, WebEngineProfile *> m_profiles; + WebEngineProfile* m_defaultProfile; - UrlRequestInterceptor *m_urlRequestInterceptor = nullptr; - QSharedPointer<BookmarksWidget> m_bookmarksManager; - QSharedPointer<DownloadsWidget> m_downloadManager; - BlockerManager *m_blocklistManager = nullptr; - - QObject *m_plugin = nullptr; +// UrlRequestInterceptor *m_urlRequestInterceptor = nullptr; +// std::shared_ptr<BookmarksWidget> m_bookmarksManager; +// std::shared_ptr<DownloadsWidget> m_downloadManager; +// BlockerManager *m_blocklistManager = nullptr; }; diff --git a/src/forms/aboutdialog.cpp b/src/forms/aboutdialog.cpp index e9c2a93..addf66c 100644 --- a/src/forms/aboutdialog.cpp +++ b/src/forms/aboutdialog.cpp @@ -20,7 +20,6 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" -#include "browser.h" AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), @@ -34,42 +33,51 @@ AboutDialog::AboutDialog(QWidget *parent) : QLabel *aboutLabel = new QLabel(this); aboutLabel->setWordWrap(true); aboutLabel->setText(tr("<h2>smolbote %1</h2>" - "<p><i>yet another Qute browser</i></p>" - "<p>Copyright (C) 2017 Xian Nox</p>" - "<p>This program comes with ABSOLUTELY NO WARRANTY. " - "This is free software, and you are welcome to redistribute it under the conditions set by the GNU GPLv3.</p>") + "<p><i>yet another Qute browser</i></p>") .arg(qApp->applicationVersion())); ui->toolBox->addItem(aboutLabel, tr("About")); - QLabel *detailsLabel = new QLabel(this); - detailsLabel->setWordWrap(true); - detailsLabel->setText(tr("<h3>Version %1</h3>" - "<p>" - "Based on Qt " QT_VERSION_STR "<br>" -#if defined __clang__ - "Compiled with Clang " __clang_version__ "<br>" -#elif defined __GNUC__ - "Compiled with GCC " __VERSION__ "<br>" -#endif - "Configuration lives in %2<br>" - "Default configuration lives in %3" - "</p>") - .arg(browser->applicationLongVersion(), - browser->settings()->configurationPath(), - browser->settings()->defaultsPath())); - ui->toolBox->addItem(detailsLabel, tr("Details")); + QLabel *licenseLabel = new QLabel(this); + licenseLabel->setWordWrap(true); + licenseLabel->setText(tr("<p>Copyright (C) 2017 Xian Nox</p>" + "<p>This program is free software, and you are welcome to use it under the conditions set by the GNU GPLv3:" + "<ul>" + "<li> the freedom to use the software for any purpose,</li>" + "<li> the freedom to change the software to suit your needs,</li>" + "<li> the freedom to share the software with anyone,</li>" + "<li> the freedom to share the changes you make, and</li>" + "<li> the responsibility to grant the same freedoms when sharing the software.</li>" + "</ul>" + "<p>You can find the full license text in LICENSE.md.</p>")); + ui->toolBox->addItem(licenseLabel, tr("License")); QLabel *libsLabel = new QLabel(this); libsLabel->setWordWrap(true); - libsLabel->setText(tr("<ul>" - "<li>Qt %1</li>" - "<li>tinytoml</li>" - "</ul>") - .arg(qVersion())); - ui->toolBox->addItem(libsLabel, tr("Libraries")); + libsLabel->setText(tr("<h3>Version %1</h3>" + "<p>" + "Based on Qt " QT_VERSION_STR "<br>" + "Compiled with %2" + "</p>" + "<p><ul>" + "<li>Qt %3</li>" + "<li>libconfig</li>" + "</ul></p>") + .arg(qApp->applicationVersion(), getCompiler(), qVersion())); + ui->toolBox->addItem(libsLabel, tr("Details")); } AboutDialog::~AboutDialog() { delete ui; } + +constexpr const char *getCompiler() +{ + if(__clang__) { + return "Clang " __clang_version__; + } else if(__GNUC__) { + return "GCC " __VERSION__; + } else { + return "unknown compiler"; + } +} diff --git a/src/forms/aboutdialog.h b/src/forms/aboutdialog.h index 98a4a49..e7b2bb3 100644 --- a/src/forms/aboutdialog.h +++ b/src/forms/aboutdialog.h @@ -32,11 +32,13 @@ class AboutDialog : public QDialog Q_OBJECT public: - explicit AboutDialog(QWidget *parent = 0); + explicit AboutDialog(QWidget *parent = nullptr); ~AboutDialog(); private: Ui::AboutDialog *ui; }; +constexpr const char* getCompiler(); + #endif // ABOUTDIALOG_H diff --git a/src/forms/profilesdialog.cpp b/src/forms/profilesdialog.cpp index 7723683..b295675 100644 --- a/src/forms/profilesdialog.cpp +++ b/src/forms/profilesdialog.cpp @@ -36,7 +36,7 @@ ProfilesDialog::ProfilesDialog(MainWindow *window, QWidget *parent) : { m_window = window; - m_view = new ProfileView(0, this); + m_view = new ProfileView(nullptr, this); // Hide the profile view because we're fancy // Give focus to the top widget because otherwise the listwidget gains focus @@ -69,10 +69,10 @@ void ProfilesDialog::loadProfiles() { ui->listWidget->clear(); - for(QString name : browser->profiles()) { - QListWidgetItem *item = new QListWidgetItem(browser->profile(name)->name(), ui->listWidget); - item->setData(Qt::UserRole, name); - } +// for(QString name : browser->profiles()) { +// QListWidgetItem *item = new QListWidgetItem(browser->profile(name)->name(), ui->listWidget); +// item->setData(Qt::UserRole, name); +// } } void ProfilesDialog::newProfile() @@ -80,19 +80,19 @@ void ProfilesDialog::newProfile() bool ok; QString name = QInputDialog::getText(this, tr("Profile Name"), tr("Profile Name:"), QLineEdit::Normal, tr("Default"), &ok); - if(ok) { - browser->profile(name); - loadProfiles(); - } +// if(ok) { +// browser->profile(name); +// loadProfiles(); +// } } void ProfilesDialog::loadSelectedProfile() { - m_window->setProfile(browser->profile(ui->listWidget->currentItem()->data(Qt::UserRole).toString())); + //m_window->setProfile(browser->profile(ui->listWidget->currentItem()->data(Qt::UserRole).toString())); } void ProfilesDialog::viewProfile(int index) { - m_view->setProfile(browser->profile(ui->listWidget->item(index)->data(Qt::UserRole).toString())); + //m_view->setProfile(browser->profile(ui->listWidget->item(index)->data(Qt::UserRole).toString())); m_view->show(); } diff --git a/src/forms/profilesdialog.h b/src/forms/profilesdialog.h index 71226e0..066fa7c 100644 --- a/src/forms/profilesdialog.h +++ b/src/forms/profilesdialog.h @@ -34,7 +34,7 @@ class ProfilesDialog : public QDialog Q_OBJECT public: - explicit ProfilesDialog(MainWindow *window, QWidget *parent = 0); + explicit ProfilesDialog(MainWindow *window, QWidget *parent = nullptr); ~ProfilesDialog(); public slots: diff --git a/src/main.cpp b/src/main.cpp index 9df24b2..837e06c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,121 +20,144 @@ #include <iostream> #include "browser.h" -#include "mainwindow.h" -#include <QApplication> #include <QCommandLineParser> +#include <QFile> +#include <QStandardPaths> +#include "mainwindow.h" -/** - * @brief printPoiToml Print the built-in config - * @param output std::ostream to print to - */ -void printPoiToml(std::ostream &output) +// read config into std::string, supports qrc +inline std::string readConfig(QString path) { - QFile conf(":/poi.toml"); + QFile conf(path); + std::string ret; if(conf.open(QIODevice::ReadOnly)) { - output << conf.readAll().toStdString() << std::endl; + ret = conf.readAll().toStdString(); conf.close(); } + return ret; +} + +bool writeUserConfig(const std::string &path, Configuration &config) +{ + // set firstRun to false so we don't re-init on every run + config.setValue<bool>("browser.firstRun", false); + + // The .path's need to be overriden because ~ doesn't translate to home + const QString &home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + + // profile.path + std::string profilePath = config.value<std::string>("profile.path").value(); + config.setValue<std::string>("profile.path", patchHome(profilePath, home.toStdString())); + + // bookmarks.path + std::string bookmarksPath = config.value<std::string>("bookmarks.path").value(); + config.setValue<std::string>("bookmarks.path", patchHome(bookmarksPath, home.toStdString())); + + // downloads.path + std::string downloadsPath = config.value<std::string>("downloads.path").value(); + config.setValue<std::string>("downloads.path", patchHome(downloadsPath, home.toStdString())); + + return config.writeUserConfiguration(path); } -const QHash<QString, QVariant> parseCommandLine(QApplication &app) +int main(int argc, char *argv[]) { + // Create application object + Browser instance(argc, argv); + QCommandLineParser parser; parser.setApplicationDescription("yet another Qt browser"); parser.addHelpOption(); parser.addVersionOption(); - QCommandLineOption configOption(QStringList() << "c" << "config", "Set configuration file.", "PATH"); + // user config, ~/.config/smolbote/smolbote.cfg or empty if there is none + QCommandLineOption configOption({"c", "config"}, "Set configuration file.", "path"); + configOption.setDefaultValue(QStandardPaths::locate(QStandardPaths::AppConfigLocation, "smolbote.cfg")); parser.addOption(configOption); - QCommandLineOption defaultConfigOption(QStringList() << "print-defaults", "Print default configuration."); + // default config, :/poi.cfg + QCommandLineOption defaultConfigOption("default-config", "Set the default configuration file.", "path"); + defaultConfigOption.setDefaultValue(":/poi.cfg"); parser.addOption(defaultConfigOption); - QCommandLineOption profileOption(QStringList() << "p" << "profile", "Use this profile.", "PROFILE"); - parser.addOption(profileOption); + // print default config, so users can easily create their overrides + QCommandLineOption printDefaultConfigOption("print-default-config", "Print default configuration."); + parser.addOption(printDefaultConfigOption); - //QCommandLineOption nopluginsOption(QStringList() << "n" << "no-plugins", "Don't load plugins"); - //parser.addOption(nopluginsOption); + // generate user config + QCommandLineOption generateUserConfigOption("generate-user-config", "Generate user configuration and exit."); + parser.addOption(generateUserConfigOption); - QCommandLineOption newInstanceOption(QStringList() << "new-instance", "Skip instance check at startup"); + QCommandLineOption profileOption({"p", "profile"}, "Use this profile.", "PROFILE"); + profileOption.setDefaultValue(""); + parser.addOption(profileOption); + + QCommandLineOption newInstanceOption("new-instance", "Skip instance check at startup"); parser.addOption(newInstanceOption); - QCommandLineOption newWindowOption(QStringList() << "in-new-window", "Open URL in new window"); + QCommandLineOption newWindowOption("in-new-window", "Open URL in new window"); parser.addOption(newWindowOption); - QCommandLineOption newTabOption(QStringList() << "in-new-tab", "Open URL in new tab"); + QCommandLineOption newTabOption("in-new-tab", "Open URL in new tab"); parser.addOption(newTabOption); parser.addPositionalArgument("URL", "URL(s) to open"); - parser.process(app); + parser.process(instance); - QHash<QString, QVariant> options; - if(parser.isSet(configOption)) { - options.insert("config", parser.value(configOption)); - } - if(parser.isSet(defaultConfigOption)) { - options.insert("print-defaults", true); - } - if(parser.isSet(profileOption)) { - options.insert("profile", parser.value(profileOption)); - } - if(parser.isSet(newInstanceOption)) { - options.insert("new-instance", true); - } - if(parser.isSet(newWindowOption)) { - options.insert("in-new-window", true); - } - if(parser.isSet(newTabOption)) { - options.insert("in-new-tab", true); - } - options.insert("urls", parser.positionalArguments()); +#ifdef QT_DEBUG + qDebug("config=%s", qUtf8Printable(parser.value(configOption))); + qDebug("default-config=%s", qUtf8Printable(parser.value(defaultConfigOption))); +#endif - return options; -} + if(parser.isSet(printDefaultConfigOption)) { + std::cout << readConfig(parser.value(defaultConfigOption)); + std::cout.flush(); + return 0; + } -int main(int argc, char *argv[]) -{ - // Create application object - Browser app(argc, argv); - - // program init - { - // parse command line options - const QHash<QString, QVariant> options = parseCommandLine(app); - - // Check for another instance unless specified not to - if(!options.contains("new-instance")) { - app.bindLocalSocket(); - if(app.isRunning()) { - qDebug("Another instance is running, returning..."); - app.sendMessage(options); - return 0; - } + std::shared_ptr<Configuration> config = std::make_shared<Configuration>(); + config->readDefaultConfiguration(readConfig(parser.value(defaultConfigOption))); + config->readUserConfiguration(parser.value(configOption).toStdString()); + instance.setConfiguration(config); + + // check if first run + if(config->value<bool>("browser.firstRun").value() || parser.isSet(generateUserConfigOption)) { + // create a user config file + QString path = parser.value(configOption); + // there may be no smolbote.cfg + if(path.isEmpty()) { + path = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/smolbote.cfg"; } - if(options.contains("print-defaults")) { - printPoiToml(std::cout); +#ifdef QT_DEBUG + qDebug("Generating user config on first run to %s", qUtf8Printable(path)); +#endif + + qDebug("Saving config to: %s - %s", qUtf8Printable(path), writeUserConfig(path.toStdString(), *config) ? "ok" : "failed"); + + if(parser.isSet(generateUserConfigOption)) { return 0; } + } - app.setWindowIcon(QIcon(QLatin1String(":/icon.svg"))); - // This lets the web view automatically scale on high-dpi displays. - app.setAttribute(Qt::AA_EnableHighDpiScaling); - - // Set configuration - app.loadSettings(options.value("config", "").toString()); + // TODO: instance check - // Load profiles - app.loadProfiles(); + instance.loadProfiles(); - // Load plugins - // if(!parser.isSet(nopluginsOption)) { - // app.loadPlugins(); - // } + MainWindow* mainWindow = instance.createWindow(); + if(parser.isSet(profileOption)) { + mainWindow->setProfile(instance.profile(parser.value(profileOption))); + } - app.createWindow(options); + if(parser.positionalArguments().isEmpty()) { + // no URLs were given + mainWindow->newTab(QUrl::fromUserInput(config->value<std::string>("profile.homepage").value().c_str())); + } else { + for(const QString &url : parser.positionalArguments()) { + mainWindow->newTab(QUrl::fromUserInput(url)); + } } - return app.exec(); + return instance.exec(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index daf344d..28a0ebb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -22,9 +22,6 @@ #include "ui_mainwindow.h" #include "widgets/mainwindowmenubar.h" #include <QMessageBox> -#include "browser.h" -#include <QWebEngineDownloadItem> -#include <QStatusBar> #include "forms/aboutdialog.h" #include <QToolButton> @@ -36,7 +33,9 @@ #include <QDockWidget> -MainWindow::MainWindow(QWidget *parent) : +#include <settings/configuration.h> + +MainWindow::MainWindow(std::shared_ptr<Configuration> config, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), navigationToolBar(new QToolBar(tr("Navigation"), this)), @@ -45,14 +44,17 @@ MainWindow::MainWindow(QWidget *parent) : m_addressBar(new AddressBar(navigationToolBar)), m_progressBar(new LoadingBar(this)) { + Q_ASSERT(config); + m_config = config; + // delete this window when it closes setAttribute(Qt::WA_DeleteOnClose, true); // set up UI ui->setupUi(this); QAction *fullscreenAction = new QAction(this); - fullscreenAction->setShortcut(QKeySequence::fromString(browser->settings()->value("window.shortcuts.fullscreen").toString())); - connect(fullscreenAction, SIGNAL(triggered(bool)), this, SLOT(toggleFullscreen())); + fullscreenAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value<std::string>("browser.shortcuts.fullscreen").value()))); + connect(fullscreenAction, &QAction::triggered, this, &MainWindow::toggleFullscreen); addAction(fullscreenAction); // Dockable widget styling @@ -61,7 +63,7 @@ MainWindow::MainWindow(QWidget *parent) : setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North); // Main menu - MainWindowMenuBar *menuBar = new MainWindowMenuBar(this); + MainWindowMenuBar *menuBar = new MainWindowMenuBar(config, this); menuBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); connect(menuBar->printAction(), &QAction::triggered, this, [&]() { @@ -71,14 +73,14 @@ MainWindow::MainWindow(QWidget *parent) : // Add the toolbars // tabToolBar: main menu and tab list - tabToolBar->setMovable(browser->settings()->value("window.ui.tabtoolbarMovable").toBool()); + tabToolBar->setMovable(m_config->value<bool>("browser.ui.tabtoolbarMovable").value()); tabToolBar->addWidget(menuBar); //tabToolBar->addWidget(tabBar); this->addToolBar(Qt::TopToolBarArea, tabToolBar); this->addToolBarBreak(Qt::TopToolBarArea); // navigationToolBar: address bar - navigationToolBar->setMovable(browser->settings()->value("window.ui.navtoolbarMovable").toBool()); + navigationToolBar->setMovable(m_config->value<bool>("browser.ui.navtoolbarMovable").value()); // page actions m_backButton = new NavigationButton(NavigationButton::BackButton, this); @@ -107,18 +109,18 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_addressBar, &AddressBar::searchTermEntered, this, [&](const QString &string) { QString term = string.mid(1); term.replace(' ', '+'); - tabBar->currentView()->load(QUrl::fromUserInput(browser->settings()->value("general.search").toString().replace("$term", term))); + //tabBar->currentView()->load(QUrl::fromUserInput(browser->settings()->value("general.search").toString().replace("$term", term))); }); connect(tabBar, SIGNAL(currentTabChanged(WebView*)), this, SLOT(handleTabChanged(WebView*))); - connect(browser->bookmarks(), &BookmarksWidget::openUrl, this, [&](const QUrl &url) { - if(this->isActiveWindow()) { - this->newTab(url); - } - }); +// connect(browser->bookmarks(), &BookmarksWidget::openUrl, this, [&](const QUrl &url) { +// if(this->isActiveWindow()) { +// this->newTab(url); +// } +// }); // Load profile - tabBar->setProfile(browser->profile(browser->settings()->value("browser.profile.default").toString())); + //tabBar->setProfile(browser->profile(browser->settings()->value("browser.profile.default").toString())); // Adding a tab here, because otherwise tabs won't show up //newTab(); @@ -127,12 +129,12 @@ MainWindow::MainWindow(QWidget *parent) : // shortcuts QAction *focusAddressAction = new QAction(this); - focusAddressAction->setShortcut(QKeySequence::fromString(browser->settings()->value("window.shortcuts.focusAddress").toString())); + //focusAddressAction->setShortcut(QKeySequence::fromString(browser->settings()->value("window.shortcuts.focusAddress").toString())); connect(focusAddressAction, SIGNAL(triggered(bool)), this, SLOT(focusAddress())); addAction(focusAddressAction); - resize(browser->settings()->value("window.width").toInt(), browser->settings()->value("window.height").toInt()); - if(browser->settings()->value("window.maximized").toBool()) { + resize(m_config->value<int>("browser.window.width").value(), m_config->value<int>("browser.window.height").value()); + if(m_config->value<bool>("browser.window.maximized").value()) { showMaximized(); } } @@ -144,7 +146,7 @@ MainWindow::~MainWindow() QList<QDockWidget*> allDockWidgets = findChildren<QDockWidget*>(); for(QDockWidget *w : allDockWidgets) { if(w->widget()) { - w->widget()->setParent(0); + w->widget()->setParent(nullptr); } } @@ -224,7 +226,7 @@ void MainWindow::newWindow(const QUrl &url) { QHash<QString, QVariant> options; options.insert("urls", url); - browser->createWindow(options); + //browser->createWindow(options); } void MainWindow::handleTabChanged(WebView *view) @@ -236,7 +238,7 @@ void MainWindow::handleTabChanged(WebView *view) // centralWidget can be a nullptr if(centralWidget()) { // clear the parent of the central widget so it doesn't get deleted - centralWidget()->setParent(0); + centralWidget()->setParent(nullptr); // disconnect signals disconnect(centralWidget()); @@ -262,5 +264,9 @@ void MainWindow::handleTabChanged(WebView *view) void MainWindow::handleTitleUpdated(const QString &title) { - setWindowTitle(browser->settings()->value("window.title").toString().replace("title", title).replace("profile", tabBar->profile()->name())); + QString t = QString::fromStdString(m_config->value<std::string>("browser.window.title").value()); + t.replace("title", title); + t.replace("profile", tabBar->profile()->name()); + setWindowTitle(t); + //setWindowTitle(browser->settings()->value("window.title").toString().replace("title", title).replace("profile", tabBar->profile()->name())); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 5994f44..4a0c87d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -34,17 +34,20 @@ #include "navigation/navigationbutton.h" +#include <memory> + namespace Ui { class MainWindow; } +class Configuration; class MainWindow : public QMainWindow { Q_OBJECT public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); + MainWindow(std::shared_ptr<Configuration> config, QWidget *parent = nullptr); + ~MainWindow() override; void addTabbedDock(Qt::DockWidgetArea area, QWidget *widget); @@ -80,6 +83,7 @@ private: LoadingBar *m_progressBar; bool m_tabBarAdded = false; + std::shared_ptr<Configuration> m_config; }; #endif // MAINWINDOW_H diff --git a/src/singleapplication.cpp b/src/singleapplication.cpp index 331268b..2c74e6c 100644 --- a/src/singleapplication.cpp +++ b/src/singleapplication.cpp @@ -29,9 +29,11 @@ SingleApplication::SingleApplication(int &argc, char **argv) : QApplication(argc SingleApplication::~SingleApplication() { - if(m_localServer->isListening()) { - m_localServer->close(); - QLocalServer::removeServer(LOCALSERVER_KEY); + if(m_localServer) { + if(m_localServer->isListening()) { + m_localServer->close(); + QLocalServer::removeServer(LOCALSERVER_KEY); + } } } diff --git a/src/singleapplication.h b/src/singleapplication.h index b512b34..aca1d3e 100644 --- a/src/singleapplication.h +++ b/src/singleapplication.h @@ -46,7 +46,7 @@ private: const int LOCALSERVER_TIMEOUT = 500; const QString LOCALSERVER_KEY = "smolbote_socket"; - QLocalServer *m_localServer; + QLocalServer *m_localServer = nullptr; }; #endif // SINGLEAPPLICATION_H diff --git a/src/webengine/webengineprofile.cpp b/src/webengine/webengineprofile.cpp index 62f744e..52a740f 100644 --- a/src/webengine/webengineprofile.cpp +++ b/src/webengine/webengineprofile.cpp @@ -19,113 +19,104 @@ ******************************************************************************/ #include "webengineprofile.h" -#include "browser.h" #include <QSettings> #include <QWebEngineSettings> -#include <QFile> WebEngineProfile::WebEngineProfile(QObject *parent) : QWebEngineProfile(parent) { m_name = tr("Off-the-record"); - // Off-the-record profiles have no persistent path +#ifdef QT_DEBUG + qDebug("Creating off-the-record profile"); +#endif - m_homepage = browser->settings()->value("browser.profile.new.homepage").toUrl(); - m_newtab = browser->settings()->value("browser.profile.new.newtab").toUrl(); + // Off-the-record profiles have no persistent path } -WebEngineProfile::WebEngineProfile(const QString &name, QObject *parent) : +WebEngineProfile::WebEngineProfile(const QString &name, const QString &path, QObject *parent) : QWebEngineProfile(name, parent) { m_name = name; - setPersistentStoragePath(browser->settings()->value("browser.profile.storagePath").toString() + name); - setCachePath(browser->settings()->value("browser.profile.cachePath").toString() + name); + +#ifdef QT_DEBUG + qDebug("Creating profile %s", qUtf8Printable(m_name)); +#endif + + setPersistentStoragePath(path + "/storage"); + setCachePath(path + "/cache"); // Read profile settings - QString profileIniPath = browser->settings()->value("browser.profile.path").toString() + name + "/profile.ini"; - - // If none exist, use the defaults - if(!QFile::exists(profileIniPath)) { - qDebug("Creating new profile..."); - m_homepage = browser->settings()->value("browser.profile.new.homepage").toUrl(); - m_newtab = browser->settings()->value("browser.profile.new.newtab").toUrl(); - - // Else read them - } else { - - qDebug("Reading profile from [%s]", qUtf8Printable(profileIniPath)); - QSettings config(profileIniPath, QSettings::IniFormat); - - m_homepage = config.value("homepage", m_homepage).toUrl(); - m_newtab = config.value("newtab", m_newtab).toUrl(); - - config.beginGroup("http"); - setHttpUserAgent(config.value("userAgent").toString()); - setHttpAcceptLanguage(config.value("accept-lang").toString()); - { - QString cacheType = config.value("cacheType").toString(); - if(cacheType == "memory") { - setHttpCacheType(MemoryHttpCache); - } else if(cacheType == "disk") { - setHttpCacheType(DiskHttpCache); - } else if(cacheType == "disabled") { - setHttpCacheType(NoCache); - } + const QString profileIniPath = path + "/profile.ini"; + qDebug("Reading profile from [%s]", qUtf8Printable(profileIniPath)); + QSettings config(profileIniPath, QSettings::IniFormat); + + m_homepage = config.value("homepage", m_homepage).toUrl(); + m_newtab = config.value("newtab", m_newtab).toUrl(); + + config.beginGroup("http"); + setHttpUserAgent(config.value("userAgent").toString()); + setHttpAcceptLanguage(config.value("accept-lang").toString()); + { + QString cacheType = config.value("cacheType").toString(); + if(cacheType == "memory") { + setHttpCacheType(MemoryHttpCache); + } else if(cacheType == "disk") { + setHttpCacheType(DiskHttpCache); + } else if(cacheType == "disabled") { + setHttpCacheType(NoCache); } - setHttpCacheMaximumSize(config.value("cacheSize").toInt()); - config.endGroup(); // http - - config.beginGroup("policy"); - { - QString cookies = config.value("cookies").toString(); - if(cookies == "disabled") { - setPersistentCookiesPolicy(NoPersistentCookies); - } else if(cookies == "allow") { - setPersistentCookiesPolicy(AllowPersistentCookies); - } else if(cookies == "force") { - setPersistentCookiesPolicy(ForcePersistentCookies); - } + } + setHttpCacheMaximumSize(config.value("cacheSize").toInt()); + config.endGroup(); // http + + config.beginGroup("policy"); + { + QString cookies = config.value("cookies").toString(); + if(cookies == "disabled") { + setPersistentCookiesPolicy(NoPersistentCookies); + } else if(cookies == "allow") { + setPersistentCookiesPolicy(AllowPersistentCookies); + } else if(cookies == "force") { + setPersistentCookiesPolicy(ForcePersistentCookies); } - config.endGroup(); // policy - - config.beginGroup("attributes"); - settings()->setAttribute(QWebEngineSettings::AutoLoadImages, config.value("autoLoadImages", true).toBool()); - settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, config.value("javascriptEnabled", true).toBool()); - settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, config.value("javascriptCanOpenWindows", true).toBool()); - settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, config.value("javascriptCanAccessClipboard", false).toBool()); - settings()->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, config.value("linksIncludedInFocusChain", true).toBool()); - settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, config.value("localStorageEnabled", true).toBool()); - settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, config.value("localContentCanAccessRemoteUrls", false).toBool()); - settings()->setAttribute(QWebEngineSettings::XSSAuditingEnabled, config.value("xssAuditingEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, config.value("spatialNavigationEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, config.value("localContentCanAccessFileUrls", true).toBool()); - settings()->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, config.value("hyperlinkAuditingEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, config.value("scrollAnimatorEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, config.value("errorPageEnabled", true).toBool()); - settings()->setAttribute(QWebEngineSettings::PluginsEnabled, config.value("pluginsEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, config.value("fullscreenSupportEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, config.value("screenCaptureEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::WebGLEnabled, config.value("webglEnabled", true).toBool()); - settings()->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, config.value("accelerated2dCanvasEnabled", true).toBool()); - settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, config.value("autoLoadIconsForPage", true).toBool()); - settings()->setAttribute(QWebEngineSettings::TouchIconsEnabled, config.value("touchIconsEnabled", false).toBool()); - settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, config.value("focusOnNavigationEnabled", true).toBool()); - settings()->setAttribute(QWebEngineSettings::PrintElementBackgrounds, config.value("printElementBackgrounds", true).toBool()); - settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, config.value("allowRunningInsecureContent", false).toBool()); - config.endGroup(); // attributes - - } // QFile::exists(profilePath) + } + config.endGroup(); // policy + + config.beginGroup("attributes"); + settings()->setAttribute(QWebEngineSettings::AutoLoadImages, config.value("autoLoadImages", true).toBool()); + settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, config.value("javascriptEnabled", true).toBool()); + settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, config.value("javascriptCanOpenWindows", true).toBool()); + settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, config.value("javascriptCanAccessClipboard", false).toBool()); + settings()->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, config.value("linksIncludedInFocusChain", true).toBool()); + settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, config.value("localStorageEnabled", true).toBool()); + settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, config.value("localContentCanAccessRemoteUrls", false).toBool()); + settings()->setAttribute(QWebEngineSettings::XSSAuditingEnabled, config.value("xssAuditingEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, config.value("spatialNavigationEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, config.value("localContentCanAccessFileUrls", true).toBool()); + settings()->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, config.value("hyperlinkAuditingEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, config.value("scrollAnimatorEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, config.value("errorPageEnabled", true).toBool()); + settings()->setAttribute(QWebEngineSettings::PluginsEnabled, config.value("pluginsEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, config.value("fullscreenSupportEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, config.value("screenCaptureEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::WebGLEnabled, config.value("webglEnabled", true).toBool()); + settings()->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, config.value("accelerated2dCanvasEnabled", true).toBool()); + settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, config.value("autoLoadIconsForPage", true).toBool()); + settings()->setAttribute(QWebEngineSettings::TouchIconsEnabled, config.value("touchIconsEnabled", false).toBool()); + settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, config.value("focusOnNavigationEnabled", true).toBool()); + settings()->setAttribute(QWebEngineSettings::PrintElementBackgrounds, config.value("printElementBackgrounds", true).toBool()); + settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, config.value("allowRunningInsecureContent", false).toBool()); + config.endGroup(); // attributes + + } WebEngineProfile::~WebEngineProfile() { - if(!isOffTheRecord()) { + if(shouldSaveProfile) { saveProfile(); } - if(m_profileDialog != nullptr) { - m_profileDialog->deleteLater(); - } } QString WebEngineProfile::name() const diff --git a/src/webengine/webengineprofile.h b/src/webengine/webengineprofile.h index f457f53..c054349 100644 --- a/src/webengine/webengineprofile.h +++ b/src/webengine/webengineprofile.h @@ -23,14 +23,13 @@ #include <QWebEngineProfile> #include <QUrl> -#include "forms/profileview.h" class WebEngineProfile : public QWebEngineProfile { Q_OBJECT public: - explicit WebEngineProfile(QObject *parent = Q_NULLPTR); - explicit WebEngineProfile(const QString &name, QObject *parent = Q_NULLPTR); + explicit WebEngineProfile(QObject *parent = nullptr); + explicit WebEngineProfile(const QString &name, const QString &path, QObject *parent = nullptr); ~WebEngineProfile(); @@ -49,9 +48,9 @@ public slots: private: QString m_name; + bool shouldSaveProfile = false; QUrl m_homepage = QUrl("about:blank"); QUrl m_newtab = QUrl("about:blank"); - ProfileView *m_profileDialog = nullptr; }; #endif // WEBENGINEPROFILE_H diff --git a/src/widgets/mainwindowmenubar.cpp b/src/widgets/mainwindowmenubar.cpp index 964d9fd..c233d29 100644 --- a/src/widgets/mainwindowmenubar.cpp +++ b/src/widgets/mainwindowmenubar.cpp @@ -19,49 +19,61 @@ ******************************************************************************/ #include "mainwindowmenubar.h" +#include <QApplication> #include <QMenu> -#include "browser.h" #include <QInputDialog> #include "forms/profilesdialog.h" - -#include "interfaces.h" - #include "mainwindow.h" +#include <settings/configuration.h> -MainWindowMenuBar::MainWindowMenuBar(MainWindow *parent) : +MainWindowMenuBar::MainWindowMenuBar(std::shared_ptr<Configuration> config, MainWindow *parent) : QMenuBar(parent) { - m_parentWindow = parent; + Q_ASSERT(config); + Q_ASSERT(parent); // Browser menu QMenu *browserMenu = new QMenu(qApp->applicationName(), this); addMenu(browserMenu); - browserMenu->addAction(tr("New Window"), parent, SLOT(newWindow()), QKeySequence::fromString(browser->settings()->value("window.shortcuts.windowNew").toString())); - browserMenu->addAction(tr("New Tab"), parent, SLOT(newTab()), QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabNew").toString())); + + QAction *newWindowAction = browserMenu->addAction(tr("New Window")); + connect(newWindowAction, &QAction::triggered, parent, [parent]() { + parent->newWindow(); + }); + newWindowAction->setShortcut(QKeySequence(config->value<std::string>("browser.shortcuts.newWindow").value().c_str())); + + QAction *newTabAction = browserMenu->addAction(tr("New Tab")); + connect(newTabAction, &QAction::triggered, parent, [parent]() { + parent->newTab(); + }); + newTabAction->setShortcut(QKeySequence(config->value<std::string>("browser.shortcuts.newTab").value().c_str())); + browserMenu->addSeparator(); - browserMenu->addAction(tr("About"), parent, SLOT(about()), QKeySequence(tr("F1"))); - browserMenu->addAction(tr("About Qt"), qApp, SLOT(aboutQt())); + browserMenu->addAction(tr("About"), parent, &MainWindow::about, QKeySequence(config->value<std::string>("browser.shortcuts.about").value().c_str())); + browserMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt); browserMenu->addSeparator(); - browserMenu->addAction(tr("Quit"), qApp, SLOT(quit()), QKeySequence::fromString(browser->settings()->value("window.shortcuts.windowClose").toString())); + + QAction *quitAction = browserMenu->addAction(tr("Quit"), qApp, &QApplication::quit); + quitAction->setShortcut(QKeySequence(config->value<std::string>("browser.shortcuts.quit").value().c_str())); // Tools menu QMenu *toolsMenu = new QMenu(tr("Tools"), this); addMenu(toolsMenu); QAction *downloadsAction = toolsMenu->addAction(tr("Downloads")); downloadsAction->setParent(parent); - downloadsAction->setShortcut(QKeySequence::fromString(browser->settings()->value("downloads.dialogShortcut").toString())); - connect(downloadsAction, &QAction::triggered, this, [&]() { - m_parentWindow->addTabbedDock(Qt::RightDockWidgetArea, browser->downloads()); - }); + //downloadsAction->setShortcut(QKeySequence::fromString(browser->settings()->value("downloads.dialogShortcut").toString())); + //connect(downloadsAction, &QAction::triggered, this, [&]() { + // m_parentWindow->addTabbedDock(Qt::RightDockWidgetArea, browser->downloads()); + //}); QAction *bookmarksAction = toolsMenu->addAction(tr("Bookmarks")); bookmarksAction->setParent(parent); - bookmarksAction->setShortcut(QKeySequence(browser->settings()->value("bookmarks.dialogShortcut").toString())); - connect(bookmarksAction, &QAction::triggered, this, [&]() { - m_parentWindow->addTabbedDock(Qt::RightDockWidgetArea, browser->bookmarks()); - }); + //bookmarksAction->setShortcut(QKeySequence(browser->settings()->value("bookmarks.dialogShortcut").toString())); + //connect(bookmarksAction, &QAction::triggered, this, [&]() { + // m_parentWindow->addTabbedDock(Qt::RightDockWidgetArea, browser->bookmarks()); + //}); toolsMenu->addSeparator(); - toolsMenu->addAction(tr("Filter"), browser->blocklists(), SLOT(show()), QKeySequence::fromString(browser->settings()->value("blocker.shortcut").toString())); + //toolsMenu->addAction(tr("Filter"), browser->blocklists(), SLOT(show()), QKeySequence::fromString(browser->settings()->value("blocker.shortcut").toString())); // Plugins // if(qApp->plugin("")) { @@ -78,7 +90,7 @@ MainWindowMenuBar::MainWindowMenuBar(MainWindow *parent) : // Profile menu QMenu *profileMenu = new QMenu(tr("Profile"), this); addMenu(profileMenu); - profileMenu->addAction(tr("Profiles"), this, SLOT(handleLoadProfile())); + //profileMenu->addAction(tr("Profiles"), this, SLOT(handleLoadProfile())); // Page menu QMenu *pageMenu = new QMenu(tr("Page"), this); @@ -93,9 +105,9 @@ QAction *MainWindowMenuBar::printAction() return m_printAction; } -void MainWindowMenuBar::handleLoadProfile() +void MainWindowMenuBar::handleLoadProfile(MainWindow *window) { - ProfilesDialog *dlg = new ProfilesDialog(m_parentWindow, this); + ProfilesDialog *dlg = new ProfilesDialog(window, this); dlg->exec(); // bool ok; diff --git a/src/widgets/mainwindowmenubar.h b/src/widgets/mainwindowmenubar.h index 43c786b..e5e5f38 100644 --- a/src/widgets/mainwindowmenubar.h +++ b/src/widgets/mainwindowmenubar.h @@ -22,22 +22,22 @@ #define MAINWINDOWMENUBAR_H #include <QMenuBar> +#include <memory> class MainWindow; +class Configuration; class MainWindowMenuBar : public QMenuBar { Q_OBJECT public: - explicit MainWindowMenuBar(MainWindow *parent = nullptr); + explicit MainWindowMenuBar(std::shared_ptr<Configuration> config, MainWindow *parent = nullptr); QAction *printAction(); private slots: - void handleLoadProfile(); + void handleLoadProfile(MainWindow *window); private: - MainWindow *m_parentWindow; - QAction *m_printAction; }; diff --git a/src/widgets/webviewtabbar.cpp b/src/widgets/webviewtabbar.cpp index 1367e40..f8d3dc1 100644 --- a/src/widgets/webviewtabbar.cpp +++ b/src/widgets/webviewtabbar.cpp @@ -19,7 +19,6 @@ ******************************************************************************/ #include "webviewtabbar.h" -#include "browser.h" #include <QAction> #include <QContextMenuEvent> #include <QMenu> @@ -40,19 +39,19 @@ WebViewTabBar::WebViewTabBar(WebEngineProfile *profile, QWidget *parent) : connect(this, SIGNAL(tabMoved(int,int)), this, SLOT(updateVectorArrangement(int,int))); QShortcut *tabCloseShortcut = new QShortcut(this); - tabCloseShortcut->setKey(QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabClose").toString())); + //tabCloseShortcut->setKey(QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabClose").toString())); connect(tabCloseShortcut, &QShortcut::activated, [this]() { this->removeTab(currentIndex()); }); QShortcut *tabLeftShortcut = new QShortcut(this); - tabLeftShortcut->setKey(QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabLeft").toString())); + //tabLeftShortcut->setKey(QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabLeft").toString())); connect(tabLeftShortcut, &QShortcut::activated, [this]() { this->setCurrentIndex(currentIndex()-1); }); QShortcut *tabRightShortcut = new QShortcut(this); - tabRightShortcut->setKey(QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabRight").toString())); + //tabRightShortcut->setKey(QKeySequence::fromString(browser->settings()->value("window.shortcuts.tabRight").toString())); connect(tabRightShortcut, &QShortcut::activated, [this]() { this->setCurrentIndex(currentIndex()+1); }); |