From 7ef5d0b10ae214f7c58aa77fb84547be1388f2d6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 17:20:27 +0900 Subject: Now really switching to the new toolchain: C++14, GCC/Clang 5, Qt 5.6 Also a bit of code tightening with some C++14 (but not only) things. --- CMakeLists.txt | 35 ++++++++++------------------------- room.cpp | 13 ++++++------- room.h | 14 +++++++------- 3 files changed, 23 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e33c4e0e..7750b7ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,6 @@ -cmake_minimum_required(VERSION 2.8.11) # Maybe works with even older versions +cmake_minimum_required(VERSION 3.1) -project(qmatrixclient) -enable_language(CXX) +project(qmatrixclient CXX) include(CheckCXXCompilerFlag) @@ -19,30 +18,16 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "MinSizeRel" "RelWithDebInfo") endif() -# Setup command line parameters for the compiler and linker -CHECK_CXX_COMPILER_FLAG("-Wall" WALL_FLAG_SUPPORTED) -if ( WALL_FLAG_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )-Wall($| )") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -endif ( ) -CHECK_CXX_COMPILER_FLAG("-Wpedantic" PEDANTIC_FLAG_SUPPORTED) -if ( PEDANTIC_FLAG_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )pedantic($| )") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic") -endif ( ) -CHECK_CXX_COMPILER_FLAG("-Werror=return-type" WERROR_FLAG_SUPPORTED) -if ( WERROR_FLAG_SUPPORTED ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type") -endif ( ) +foreach (FLAG all pedantic error=return-type) + CHECK_CXX_COMPILER_FLAG("-W${FLAG}" WARN_${FLAG}_SUPPORTED) + if ( WARN_${FLAG}_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )-W?${FLAG}($| )") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W${FLAG}") + endif () +endforeach () -if ( CMAKE_VERSION VERSION_LESS "3.1" ) - CHECK_CXX_COMPILER_FLAG("-std=c++11" STD_FLAG_SUPPORTED) - if ( STD_FLAG_SUPPORTED ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - endif ( STD_FLAG_SUPPORTED ) -else ( CMAKE_VERSION VERSION_LESS "3.1" ) - set(CMAKE_CXX_STANDARD 11) -endif ( CMAKE_VERSION VERSION_LESS "3.1" ) +set(CMAKE_CXX_STANDARD 14) -find_package(Qt5 5.2.1 REQUIRED Network Gui) +find_package(Qt5 5.6 REQUIRED Network Gui) get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) message( STATUS ) diff --git a/room.cpp b/room.cpp index d39e6d32..45521a73 100644 --- a/room.cpp +++ b/room.cpp @@ -457,7 +457,7 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u) emit q->memberRenamed(formerNamesakes[0]); } -inline QByteArray makeErrorStr(const Event& e, QByteArray msg) +inline auto makeErrorStr(const Event& e, QByteArray msg) { return msg.append("; event dump follows:\n").append(e.originalJson()); } @@ -499,7 +499,7 @@ void Room::Private::addMember(User *u) { insertMemberIntoMap(u); connect(u, &User::nameChanged, q, - [=] (User* u, const QString& newName) { renameMember(u, newName); }); + bind(&Private::renameMember, this, _1, _2)); emit q->userAdded(u); } } @@ -660,7 +660,7 @@ void Room::Private::getPreviousContent(int limit) { roomMessagesJob = connection->callApi(id, prevBatch, limit); - connect( roomMessagesJob, &RoomMessagesJob::result, [=]() { + connect( roomMessagesJob, &RoomMessagesJob::result, [=] { if( !roomMessagesJob->error() ) { addHistoricalMessageEvents(roomMessagesJob->releaseEvents()); @@ -780,8 +780,7 @@ void Room::Private::processRedaction(RoomEventPtr redactionEvent) } auto keepContentKeys = find_if(keepContentKeysMap.begin(), keepContentKeysMap.end(), - [&](const std::pair& t) - { return ti->type() == t.first; } ); + [&ti](const auto& t) { return ti->type() == t.first; } ); if (keepContentKeys == keepContentKeysMap.end()) { originalJson.remove("content"); @@ -1226,9 +1225,9 @@ bool MemberSorter::operator()(User *u1, User *u2) const { auto n1 = room->roomMembername(u1); auto n2 = room->roomMembername(u2); - if (n1[0] == '@') + if (n1.startsWith('@')) n1.remove(0, 1); - if (n2[0] == '@') + if (n2.startsWith('@')) n2.remove(0, 1); return n1.localeAwareCompare(n2) < 0; } diff --git a/room.h b/room.h index 08327917..77d56377 100644 --- a/room.h +++ b/room.h @@ -18,8 +18,9 @@ #pragma once -#include -#include +#include "jobs/syncjob.h" +#include "events/roommessageevent.h" +#include "joinstate.h" #include #include @@ -27,9 +28,9 @@ #include #include -#include "jobs/syncjob.h" -#include "events/roommessageevent.h" -#include "joinstate.h" +#include +#include +#include namespace QMatrixClient { @@ -58,8 +59,7 @@ namespace QMatrixClient // Used for event redaction RoomEventPtr replaceEvent(RoomEventPtr&& other) { - evt.swap(other); - return move(other); + return std::exchange(evt, std::move(other)); } private: -- cgit v1.2.3 From b1014408917dd900f029924802433321f1eb5ac4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 19:01:55 +0900 Subject: Fix CI Old GCC was still invoked; qmake-based builds weren't switched to C++14. --- .travis.yml | 17 ++++++++++------- libqmatrixclient.pri | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f033f3dd..e4d66084 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,5 @@ language: cpp -os: [ linux, osx ] - -compiler: [ gcc, clang ] - addons: apt: sources: @@ -14,10 +10,17 @@ addons: - qt56base matrix: - exclude: [ { os: osx, compiler: gcc } ] + include: + - os: linux + compiler: gcc + env: [ ENV_EVAL="CC=gcc-5 && CXX=g++-5" ] + - os: linux + compiler: clang + - os: osx + env: [ ENV_EVAL="brew update && brew install qt5 && PATH=/usr/local/opt/qt/bin" ] -install: -- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; brew install qt5; export PATH="$PATH:/usr/local/opt/qt/bin"; fi +before_install: +- eval "${ENV_EVAL}" - if [ "$TRAVIS_OS_NAME" = "linux" ]; then . /opt/qt56/bin/qt56-env.sh; fi script: diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 49442197..414e3c08 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -1,5 +1,5 @@ QT += network -CONFIG += c++11 warn_on rtti_off +CONFIG += c++14 warn_on rtti_off INCLUDEPATH += $$PWD -- cgit v1.2.3 From 155da0f2bdd831f59defd784e98fa9d4e7cea97e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 19:03:10 +0900 Subject: Connection: Be more accurate with lambda captures --- connection.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/connection.cpp b/connection.cpp index b6239762..3083eeba 100644 --- a/connection.cpp +++ b/connection.cpp @@ -125,7 +125,7 @@ void Connection::resolveServer(const QString& mxidOrDomain) dns->setType(QDnsLookup::SRV); dns->setName("_matrix._tcp." + domain); - connect(dns, &QDnsLookup::finished, [this,dns,maybeBaseUrl]() { + connect(dns, &QDnsLookup::finished, [this,dns,&maybeBaseUrl]() { QUrl baseUrl { maybeBaseUrl }; if (dns->error() == QDnsLookup::NoError && dns->serviceRecords().isEmpty()) @@ -151,7 +151,7 @@ void Connection::connectToServer(const QString& user, const QString& password, const QString& deviceId) { checkAndConnect(user, - [=] { + [&] { doConnectToServer(user, password, initialDeviceName, deviceId); }); } @@ -163,12 +163,12 @@ void Connection::doConnectToServer(const QString& user, const QString& password, user, /*medium*/ "", /*address*/ "", password, /*token*/ "", deviceId, initialDeviceName); connect(loginJob, &BaseJob::success, this, - [=] { + [this, loginJob] { d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); }); connect(loginJob, &BaseJob::failure, this, - [=] { + [this, loginJob] { emit loginError(loginJob->errorString()); }); } @@ -178,7 +178,7 @@ void Connection::connectWithToken(const QString& userId, const QString& deviceId) { checkAndConnect(userId, - [=] { d->connectWithToken(userId, accessToken, deviceId); }); + [&] { d->connectWithToken(userId, accessToken, deviceId); }); } void Connection::Private::connectWithToken(const QString& user, @@ -223,7 +223,7 @@ void Connection::checkAndConnect(const QString& userId, void Connection::logout() { auto job = callApi(); - connect( job, &LogoutJob::success, this, [=] { + connect( job, &LogoutJob::success, this, [this] { stopSync(); emit loggedOut(); }); @@ -238,13 +238,13 @@ void Connection::sync(int timeout) const QString filter { R"({"room": { "timeline": { "limit": 100 } } })" }; auto job = d->syncJob = callApi(d->data->lastEvent(), filter, timeout); - connect( job, &SyncJob::success, [=] () { + connect( job, &SyncJob::success, [this, job] { onSyncSuccess(job->takeData()); d->syncJob = nullptr; emit syncDone(); }); connect( job, &SyncJob::retryScheduled, this, &Connection::networkError); - connect( job, &SyncJob::failure, [=] () { + connect( job, &SyncJob::failure, [this, job] { d->syncJob = nullptr; if (job->error() == BaseJob::ContentAccessError) emit loginError(job->errorString()); @@ -286,7 +286,7 @@ JoinRoomJob* Connection::joinRoom(const QString& roomAlias) { auto job = callApi(roomAlias); connect(job, &JoinRoomJob::success, - this, [=] { provideRoom(job->roomId(), JoinState::Join); }); + this, [this, job] { provideRoom(job->roomId(), JoinState::Join); }); return job; } @@ -326,13 +326,12 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) { auto leaveJob = joinedRoom->leaveRoom(); connect(leaveJob, &BaseJob::success, - this, [=] { forgetJob->start(connectionData()); }); - connect(leaveJob, &BaseJob::failure, - this, [=] { forgetJob->abandon(); }); + this, [this, forgetJob] { forgetJob->start(connectionData()); }); + connect(leaveJob, &BaseJob::failure, forgetJob, &BaseJob::abandon); } else forgetJob->start(connectionData()); - connect(forgetJob, &BaseJob::success, this, [=] + connect(forgetJob, &BaseJob::success, this, [this, &id] { // If the room happens to be in the map (possible in both forms), // delete the found object(s). -- cgit v1.2.3 From 37e807b7e4b8991353802b38da226ef47b8848ec Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 20:00:24 +0900 Subject: BaseJob: consolidate job timeout configuration This prepares the backend to enable timeouts/retry intervals configurable from clients. --- jobs/basejob.cpp | 18 ++++++++++++------ jobs/basejob.h | 6 ++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/jobs/basejob.cpp b/jobs/basejob.cpp index 9d5c5ed6..3ea13c83 100644 --- a/jobs/basejob.cpp +++ b/jobs/basejob.cpp @@ -53,6 +53,7 @@ class BaseJob::Private { } void sendRequest(); + const JobTimeoutConfig& getCurrentTimeoutConfig() const; const ConnectionData* connection = nullptr; @@ -69,8 +70,10 @@ class BaseJob::Private QTimer timer; QTimer retryTimer; - size_t maxRetries = 3; - size_t retriesTaken = 0; + QVector errorStrategy = + { { 90, 5 }, { 90, 10 }, { 120, 30 } }; + int maxRetries = errorStrategy.size(); + int retriesTaken = 0; LoggingCategory logCat = JOBS; }; @@ -297,16 +300,19 @@ void BaseJob::finishJob() deleteLater(); } +const JobTimeoutConfig& BaseJob::Private::getCurrentTimeoutConfig() const +{ + return errorStrategy[std::min(retriesTaken, errorStrategy.size() - 1)]; +} + BaseJob::duration_t BaseJob::getCurrentTimeout() const { - static const std::array timeouts = { 90, 90, 120, 120 }; - return timeouts[std::min(d->retriesTaken, timeouts.size() - 1)] * 1000; + return d->getCurrentTimeoutConfig().jobTimeout * 1000; } BaseJob::duration_t BaseJob::getNextRetryInterval() const { - static const std::array intervals = { 5, 10, 30 }; - return intervals[std::min(d->retriesTaken, intervals.size() - 1)] * 1000; + return d->getCurrentTimeoutConfig().nextRetryInterval * 1000; } BaseJob::duration_t BaseJob::millisToRetry() const diff --git a/jobs/basejob.h b/jobs/basejob.h index 66812774..aa4894bd 100644 --- a/jobs/basejob.h +++ b/jobs/basejob.h @@ -36,6 +36,12 @@ namespace QMatrixClient enum class HttpVerb { Get, Put, Post, Delete }; + struct JobTimeoutConfig + { + int jobTimeout; + int nextRetryInterval; + }; + class BaseJob: public QObject { Q_OBJECT -- cgit v1.2.3 From 72ac40aec4685781d8d669cb69a70c6baf167500 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 20:12:58 +0900 Subject: Room: Remove C++14 code from the header file This will provide some backwards-compatibility to clients that are not ready to move _their_ code to C++14 (at least, it will allow them to not add C++14 requirement to their makefiles as of yet). --- room.cpp | 5 +++++ room.h | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/room.cpp b/room.cpp index 45521a73..0024683d 100644 --- a/room.cpp +++ b/room.cpp @@ -160,6 +160,11 @@ class Room::Private } }; +RoomEventPtr TimelineItem::replaceEvent(RoomEventPtr&& other) +{ + return std::exchange(evt, std::move(other)); +} + Room::Room(Connection* connection, QString id, JoinState initialJoinState) : QObject(connection), d(new Private(connection, id, initialJoinState)) { diff --git a/room.h b/room.h index 77d56377..9a458c4e 100644 --- a/room.h +++ b/room.h @@ -57,10 +57,7 @@ namespace QMatrixClient index_t index() const { return idx; } // Used for event redaction - RoomEventPtr replaceEvent(RoomEventPtr&& other) - { - return std::exchange(evt, std::move(other)); - } + RoomEventPtr replaceEvent(RoomEventPtr&& other); private: RoomEventPtr evt; -- cgit v1.2.3 From 1b7bf10fea3dc395e8d43b16c39e6b0ac231e366 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 20:19:44 +0900 Subject: Polished warnings configuration The whole thing should go without or almost without warnings with GCC and with Clang (MSVC is another story and I don't care about it much). --- CMakeLists.txt | 6 +++--- libqmatrixclient.pri | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7750b7ad..6526fbfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,15 +18,15 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "MinSizeRel" "RelWithDebInfo") endif() -foreach (FLAG all pedantic error=return-type) +set(CMAKE_CXX_STANDARD 14) + +foreach (FLAG all pedantic extra error=return-type no-unused-parameter no-gnu-zero-variadic-macro-arguments) CHECK_CXX_COMPILER_FLAG("-W${FLAG}" WARN_${FLAG}_SUPPORTED) if ( WARN_${FLAG}_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )-W?${FLAG}($| )") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W${FLAG}") endif () endforeach () -set(CMAKE_CXX_STANDARD 14) - find_package(Qt5 5.6 REQUIRED Network Gui) get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 414e3c08..10476cc9 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -1,5 +1,6 @@ QT += network CONFIG += c++14 warn_on rtti_off +QMAKE_CXXFLAGS += -Wno-unused-parameter INCLUDEPATH += $$PWD -- cgit v1.2.3 From 95486e555f0ab5dc78a7bdc14e22d73bff5cfd7e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 20:29:18 +0900 Subject: qmake: Use -std=C++14, not -std=gnu++1y substituted for CONFIG += c++14 --- libqmatrixclient.pri | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 10476cc9..3394f60e 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -1,6 +1,6 @@ QT += network -CONFIG += c++14 warn_on rtti_off -QMAKE_CXXFLAGS += -Wno-unused-parameter +CONFIG += warn_on rtti_off +QMAKE_CXXFLAGS += -std=c++14 -Wno-unused-parameter INCLUDEPATH += $$PWD -- cgit v1.2.3 From ea6ce4d4628a06f4216335b4c72b02859852ff48 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 20:45:21 +0900 Subject: qmake: Use the compiler specified in the environment; revert to CONFIG += c++14 qmake doesn't follow standard CC and CXX environment variables and its CONFIG produces CXX_FLAGS added after QMAKE_CXX_FLAGS. Big thank you to The Qt Company :-| --- .travis.yml | 2 +- libqmatrixclient.pri | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e4d66084..1b67119d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ script: - cmake .. - cmake --build . --target all - cd .. -- qmake qmc-example.pro && make all +- qmake qmc-example.pro "QMAKE_CC = $CC" "QMAKE_CXX = $CXX" && make all notifications: webhooks: diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 3394f60e..10476cc9 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -1,6 +1,6 @@ QT += network -CONFIG += warn_on rtti_off -QMAKE_CXXFLAGS += -std=c++14 -Wno-unused-parameter +CONFIG += c++14 warn_on rtti_off +QMAKE_CXXFLAGS += -Wno-unused-parameter INCLUDEPATH += $$PWD -- cgit v1.2.3 From 9335a3beba0c15a64478458f418b648834779683 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 21:23:52 +0900 Subject: BaseJob: further minor code cleanup --- jobs/basejob.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jobs/basejob.h b/jobs/basejob.h index aa4894bd..0d669b78 100644 --- a/jobs/basejob.h +++ b/jobs/basejob.h @@ -168,7 +168,7 @@ namespace QMatrixClient * @param nextAttempt the 1-based number of attempt (will always be more than 1) * @param inMilliseconds the interval after which the next attempt will be taken */ - void retryScheduled(size_t nextAttempt, int inMilliseconds); + void retryScheduled(int nextAttempt, int inMilliseconds); /** * Emitted when the job is finished, in any case. It is used to notify @@ -185,7 +185,6 @@ namespace QMatrixClient * to avoid dangling pointers in your list. * * @param job the job that emitted this signal - * @internal * * @see success, failure */ -- cgit v1.2.3 From 53241b0b47d80b90c3834336dc3db89676e1c007 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 21:33:14 +0900 Subject: Connection: change the attempt number propagated from BaseJob --- connection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connection.h b/connection.h index eccde170..21fce741 100644 --- a/connection.h +++ b/connection.h @@ -217,7 +217,7 @@ namespace QMatrixClient void reconnected(); //< Unused; use connected() instead void loggedOut(); void loginError(QString error); - void networkError(size_t nextAttempt, int inMilliseconds); + void networkError(int retriesTaken, int inMilliseconds); void syncDone(); void syncError(QString error); -- cgit v1.2.3 From 878c1e710de54bf913e085f5d5f6a198810c968f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 21:35:21 +0900 Subject: qmake: Use QMAKE_CXX_FLAGS_WARN_ON rather than QMAKE_CXX_FLAGS Because the damn thing puts QMAKE_CXX_FLAGS before what's put by CONFIG, and CONFIG injects -W -Wall that cancels -Wno-unused-parameter. --- libqmatrixclient.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 10476cc9..f0c2417f 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -1,6 +1,6 @@ QT += network CONFIG += c++14 warn_on rtti_off -QMAKE_CXXFLAGS += -Wno-unused-parameter +QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter INCLUDEPATH += $$PWD -- cgit v1.2.3 From 961bca954677cf68a3beae29bb9e16e2a5dddf3d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Dec 2017 23:07:07 +0900 Subject: CMakeLists: Add -W to the mix --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6526fbfd..84f440f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ endif() set(CMAKE_CXX_STANDARD 14) -foreach (FLAG all pedantic extra error=return-type no-unused-parameter no-gnu-zero-variadic-macro-arguments) +foreach (FLAG all "" pedantic extra error=return-type no-unused-parameter no-gnu-zero-variadic-macro-arguments) CHECK_CXX_COMPILER_FLAG("-W${FLAG}" WARN_${FLAG}_SUPPORTED) if ( WARN_${FLAG}_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )-W?${FLAG}($| )") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W${FLAG}") -- cgit v1.2.3 From 205a9a268eb3d1c35b1decaf323619a72a33036e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 26 Dec 2017 21:12:03 +0900 Subject: Settings: factored out common declarations/definitions to macros It cannot be made in templates because property and methods names are involved. --- settings.cpp | 32 +------- settings.h | 260 ++++++++++++++++++++++++++++++----------------------------- 2 files changed, 137 insertions(+), 155 deletions(-) diff --git a/settings.cpp b/settings.cpp index ac9c091c..bf369c58 100644 --- a/settings.cpp +++ b/settings.cpp @@ -84,15 +84,9 @@ void SettingsGroup::remove(const QString& key) Settings::remove(fullKey); } -bool AccountSettings::keepLoggedIn() const -{ - return value("keep_logged_in", false).toBool(); -} - -void AccountSettings::setKeepLoggedIn(bool newSetting) -{ - setValue("keep_logged_in", newSetting); -} +QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", "", setDeviceId) +QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", "", setDeviceName) +QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) QUrl AccountSettings::homeserver() const { @@ -109,26 +103,6 @@ QString AccountSettings::userId() const return group().section('/', -1); } -QString AccountSettings::deviceId() const -{ - return value("device_id").toString(); -} - -void AccountSettings::setDeviceId(const QString& deviceId) -{ - setValue("device_id", deviceId); -} - -QString AccountSettings::deviceName() const -{ - return value("device_name").toString(); -} - -void AccountSettings::setDeviceName(const QString& deviceName) -{ - setValue("device_name", deviceName); -} - QString AccountSettings::accessToken() const { return value("access_token").toString(); diff --git a/settings.h b/settings.h index 36e29cf1..27ec9ba5 100644 --- a/settings.h +++ b/settings.h @@ -1,126 +1,134 @@ -/****************************************************************************** - * Copyright (C) 2016 Kitsune Ral - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include -#include -#include - -class QVariant; - -namespace QMatrixClient -{ - class Settings: public QSettings - { - Q_OBJECT - public: - /** - * Use this function before creating any Settings objects in order - * to setup a read-only location where configuration has previously - * been stored. This will provide an additional fallback in case of - * renaming the organisation/application. - */ - static void setLegacyNames(const QString& organizationName, - const QString& applicationName = {}); - -#if defined(_MSC_VER) && _MSC_VER < 1900 - // VS 2013 (and probably older) aren't friends with 'using' statements - // that involve private constructors - explicit Settings(QObject* parent = 0) : QSettings(parent) { } -#else - using QSettings::QSettings; -#endif - - Q_INVOKABLE void setValue(const QString &key, - const QVariant &value); - Q_INVOKABLE QVariant value(const QString &key, - const QVariant &defaultValue = {}) const; - Q_INVOKABLE bool contains(const QString& key) const; - Q_INVOKABLE QStringList childGroups() const; - - private: - static QString legacyOrganizationName; - static QString legacyApplicationName; - - protected: - QSettings legacySettings { legacyOrganizationName, - legacyApplicationName }; - }; - - class SettingsGroup: public Settings - { - public: - template - explicit SettingsGroup(const QString& path, ArgTs... qsettingsArgs) - : Settings(qsettingsArgs...) - , groupPath(path) - { } - - Q_INVOKABLE bool contains(const QString& key) const; - Q_INVOKABLE QVariant value(const QString &key, - const QVariant &defaultValue = {}) const; - Q_INVOKABLE QString group() const; - Q_INVOKABLE QStringList childGroups() const; - Q_INVOKABLE void setValue(const QString &key, - const QVariant &value); - - Q_INVOKABLE void remove(const QString& key); - - private: - QString groupPath; - }; - - class AccountSettings: public SettingsGroup - { - Q_OBJECT - Q_PROPERTY(QString userId READ userId CONSTANT) - Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId) - Q_PROPERTY(QString deviceName READ deviceName WRITE setDeviceName) - Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver) - Q_PROPERTY(bool keepLoggedIn READ keepLoggedIn WRITE setKeepLoggedIn) - /** \deprecated \sa setToken */ - Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) - public: - template - explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) - : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) - { } - - QString userId() const; - - QString deviceId() const; - void setDeviceId(const QString& deviceId); - - QString deviceName() const; - void setDeviceName(const QString& deviceName); - - QUrl homeserver() const; - void setHomeserver(const QUrl& url); - - bool keepLoggedIn() const; - void setKeepLoggedIn(bool newSetting); - - /** \deprecated \sa setToken */ - QString accessToken() const; - /** \deprecated Storing accessToken in QSettings is unsafe, - * see QMatrixClient/Quaternion#181 */ - void setAccessToken(const QString& accessToken); - Q_INVOKABLE void clearAccessToken(); - }; -} // namespace QMatrixClient +/****************************************************************************** + * Copyright (C) 2016 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include +#include +#include + +class QVariant; + +namespace QMatrixClient +{ + class Settings: public QSettings + { + Q_OBJECT + public: + /** + * Use this function before creating any Settings objects in order + * to setup a read-only location where configuration has previously + * been stored. This will provide an additional fallback in case of + * renaming the organisation/application. + */ + static void setLegacyNames(const QString& organizationName, + const QString& applicationName = {}); + +#if defined(_MSC_VER) && _MSC_VER < 1900 + // VS 2013 (and probably older) aren't friends with 'using' statements + // that involve private constructors + explicit Settings(QObject* parent = 0) : QSettings(parent) { } +#else + using QSettings::QSettings; +#endif + + Q_INVOKABLE void setValue(const QString &key, + const QVariant &value); + Q_INVOKABLE QVariant value(const QString &key, + const QVariant &defaultValue = {}) const; + Q_INVOKABLE bool contains(const QString& key) const; + Q_INVOKABLE QStringList childGroups() const; + + private: + static QString legacyOrganizationName; + static QString legacyApplicationName; + + protected: + QSettings legacySettings { legacyOrganizationName, + legacyApplicationName }; + }; + + class SettingsGroup: public Settings + { + public: + template + explicit SettingsGroup(const QString& path, ArgTs... qsettingsArgs) + : Settings(qsettingsArgs...) + , groupPath(path) + { } + + Q_INVOKABLE bool contains(const QString& key) const; + Q_INVOKABLE QVariant value(const QString &key, + const QVariant &defaultValue = {}) const; + Q_INVOKABLE QString group() const; + Q_INVOKABLE QStringList childGroups() const; + Q_INVOKABLE void setValue(const QString &key, + const QVariant &value); + + Q_INVOKABLE void remove(const QString& key); + + private: + QString groupPath; + }; + +#define QMC_DECLARE_SETTING(type, propname, setter) \ + Q_PROPERTY(type propname READ propname WRITE setter) \ + public: \ + type propname() const; \ + void setter(type newValue); \ + private: + +#define QMC_DEFINE_SETTING(classname, type, propname, qsettingname, defaultValue, setter) \ +type classname::propname() const \ +{ \ + return value(QStringLiteral(qsettingname), defaultValue).value(); \ +} \ +\ +void classname::setter(type newValue) \ +{ \ + setValue(QStringLiteral(qsettingname), newValue); \ +} \ + + class AccountSettings: public SettingsGroup + { + Q_OBJECT + Q_PROPERTY(QString userId READ userId CONSTANT) + QMC_DECLARE_SETTING(QString, deviceId, setDeviceId) + QMC_DECLARE_SETTING(QString, deviceName, setDeviceName) + QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) + /** \deprecated \sa setAccessToken */ + Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) + public: + template + explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) + : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) + { } + + QString userId() const; + + QUrl homeserver() const; + void setHomeserver(const QUrl& url); + + /** \deprecated \sa setToken */ + QString accessToken() const; + /** \deprecated Storing accessToken in QSettings is unsafe, + * see QMatrixClient/Quaternion#181 */ + void setAccessToken(const QString& accessToken); + Q_INVOKABLE void clearAccessToken(); + }; +} // namespace QMatrixClient -- cgit v1.2.3 From e1be26ce46a60d26f6e936ca0443eac135b91cef Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 26 Dec 2017 21:12:45 +0900 Subject: NetworkSettings: store proxy configuration No credentials, just type, host, and port. --- CMakeLists.txt | 1 + libqmatrixclient.pri | 6 ++++-- networksettings.cpp | 25 +++++++++++++++++++++++++ networksettings.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 networksettings.cpp create mode 100644 networksettings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 84f440f6..9fea5b0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ set(libqmatrixclient_SRCS user.cpp avatar.cpp settings.cpp + networksettings.cpp events/event.cpp events/eventcontent.cpp events/roommessageevent.cpp diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index f0c2417f..5910554c 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -32,7 +32,8 @@ HEADERS += \ $$PWD/jobs/setroomstatejob.h \ $$files($$PWD/jobs/generated/*.h, false) \ $$PWD/logging.h \ - $$PWD/settings.h + $$PWD/settings.h \ + $$PWD/networksettings.h SOURCES += \ $$PWD/connectiondata.cpp \ @@ -59,4 +60,5 @@ SOURCES += \ $$PWD/jobs/setroomstatejob.cpp \ $$files($$PWD/jobs/generated/*.cpp, false) \ $$PWD/logging.cpp \ - $$PWD/settings.cpp + $$PWD/settings.cpp \ + $$PWD/networksettings.cpp diff --git a/networksettings.cpp b/networksettings.cpp new file mode 100644 index 00000000..6b023bd5 --- /dev/null +++ b/networksettings.cpp @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "networksettings.h" + +using namespace QMatrixClient; + +QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::NoProxy, setProxyType) +QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", "", setProxyHostName) +QMC_DEFINE_SETTING(NetworkSettings, int, proxyPort, "proxy_port", -1, setProxyPort) diff --git a/networksettings.h b/networksettings.h new file mode 100644 index 00000000..e113cd9b --- /dev/null +++ b/networksettings.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "settings.h" + +#include + +Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) + +namespace QMatrixClient { + class NetworkSettings: public SettingsGroup + { + Q_OBJECT + QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) + QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) + QMC_DECLARE_SETTING(int, proxyPort, setProxyPort) + Q_PROPERTY(QString proxyHost READ proxyHostName WRITE setProxyHostName) + public: + template + explicit NetworkSettings(ArgTs... qsettingsArgs) + : SettingsGroup(QStringLiteral("Network"), qsettingsArgs...) + { } + ~NetworkSettings() override = default; + }; +} -- cgit v1.2.3 From 00bd1be842f58b87633371a76587c103533baff5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 27 Dec 2017 15:42:11 +0900 Subject: StateEvent<>: Look for prev_content in unsigned, not top-level --- events/event.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/events/event.h b/events/event.h index d4923f1f..00dc4d5c 100644 --- a/events/event.h +++ b/events/event.h @@ -254,9 +254,10 @@ namespace QMatrixClient , _content(contentJson(), std::forward(contentParams)...) { - if (obj.contains("prev_content")) + auto unsignedData = obj.value("unsigned").toObject(); + if (unsignedData.contains("prev_content")) _prev.reset(new ContentT( - obj["prev_content"].toObject(), + unsignedData.value("prev_content").toObject(), std::forward(contentParams)...)); } template -- cgit v1.2.3 From 1dce783c4ac9ca37343648114885d332bdfe7fa1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 27 Dec 2017 19:28:38 +0900 Subject: Introduce StateEventBase - a non-template base for StateEvent<> This will hold common logic for all state events, including the newly introduced repeatsState() that returns true when prev_content repeats content. This will be used to address QMatrixClient/Quaternion#245. --- events/event.cpp | 10 ++++++++++ events/event.h | 22 ++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/events/event.cpp b/events/event.cpp index e74bc114..c7345a13 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -144,3 +144,13 @@ RoomEventPtr _impl::doMakeEvent(const QJsonObject& obj) RoomAvatarEvent, EncryptionEvent, RedactionEvent> (obj, obj["type"].toString()) }; } + +StateEventBase::~StateEventBase() = default; + +bool StateEventBase::repeatsState() const +{ + auto contentJson = originalJsonObject().value("content"); + auto prevContentJson = originalJsonObject().value("unsigned") + .toObject().value("prev_content"); + return contentJson == prevContentJson; +} diff --git a/events/event.h b/events/event.h index 00dc4d5c..319c3443 100644 --- a/events/event.h +++ b/events/event.h @@ -240,8 +240,23 @@ namespace QMatrixClient RoomEvents::iterator end() { return to; } }; + class StateEventBase: public RoomEvent + { + public: + explicit StateEventBase(Type type, const QJsonObject& obj) + : RoomEvent(obj.contains("state_key") ? type : Type::Unknown, + obj) + { } + explicit StateEventBase(Type type) + : RoomEvent(type) + { } + ~StateEventBase() override = 0; + + virtual bool repeatsState() const; + }; + template - class StateEvent: public RoomEvent + class StateEvent: public StateEventBase { public: using content_type = ContentT; @@ -249,8 +264,7 @@ namespace QMatrixClient template explicit StateEvent(Type type, const QJsonObject& obj, ContentParamTs&&... contentParams) - : RoomEvent(obj.contains("state_key") ? type : Type::Unknown, - obj) + : StateEventBase(type, obj) , _content(contentJson(), std::forward(contentParams)...) { @@ -262,7 +276,7 @@ namespace QMatrixClient } template explicit StateEvent(Type type, ContentParamTs&&... contentParams) - : RoomEvent(type) + : StateEventBase(type) , _content(std::forward(contentParams)...) { } -- cgit v1.2.3 From a3b9fe1ddd2d3b0a0cbb07ffc42317b30a1a3899 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 28 Dec 2017 11:26:59 +0900 Subject: Switch from QPixmap to QImage; add convenience avatar() overloads to Room and User The switch is necessary because MediaThumbnailJob is supposed to return something that can be worked on in non-GUI threads (as is the case of QML image providers), and QPixmap is not supposed for usage out of the main thread. --- avatar.cpp | 43 +++++++++++++++++++++++++------------------ avatar.h | 3 ++- jobs/mediathumbnailjob.cpp | 11 ++++++----- jobs/mediathumbnailjob.h | 6 +++--- room.cpp | 7 ++++++- room.h | 13 +++++++++++-- user.cpp | 7 ++++++- user.h | 3 ++- 8 files changed, 61 insertions(+), 32 deletions(-) diff --git a/avatar.cpp b/avatar.cpp index b2f50a67..9490347d 100644 --- a/avatar.cpp +++ b/avatar.cpp @@ -22,20 +22,22 @@ #include "events/eventcontent.h" #include "connection.h" +#include + using namespace QMatrixClient; class Avatar::Private { public: Private(Connection* c, QIcon di) : _connection(c), _defaultIcon(di) { } - QPixmap get(int width, int height, Avatar::notifier_t notifier); + QImage get(QSize size, Avatar::notifier_t notifier); Connection* _connection; const QIcon _defaultIcon; QUrl _url; - QPixmap _originalPixmap; + QImage _originalImage; - std::vector> _scaledPixmaps; + std::vector> _scaledImages; QSize _requestedSize; bool _valid = false; @@ -49,22 +51,25 @@ Avatar::Avatar(Connection* connection, QIcon defaultIcon) Avatar::~Avatar() = default; -QPixmap Avatar::get(int width, int height, Avatar::notifier_t notifier) +QImage Avatar::get(int dimension, Avatar::notifier_t notifier) { - return d->get(width, height, notifier); + return d->get({dimension, dimension}, notifier); } -QPixmap Avatar::Private::get(int width, int height, Avatar::notifier_t notifier) +QImage Avatar::get(int width, int height, Avatar::notifier_t notifier) { - QSize size(width, height); + return d->get({width, height}, notifier); +} +QImage Avatar::Private::get(QSize size, Avatar::notifier_t notifier) +{ // FIXME: Alternating between longer-width and longer-height requests // is a sure way to trick the below code into constantly getting another // image from the server because the existing one is alleged unsatisfactory. // This is plain abuse by the client, though; so not critical for now. if( ( !(_valid || _ongoingRequest) - || width > _requestedSize.width() - || height > _requestedSize.height() ) && _url.isValid() ) + || size.width() > _requestedSize.width() + || size.height() > _requestedSize.height() ) && _url.isValid() ) { qCDebug(MAIN) << "Getting avatar from" << _url.toString(); _requestedSize = size; @@ -77,8 +82,9 @@ QPixmap Avatar::Private::get(int width, int height, Avatar::notifier_t notifier) if (_ongoingRequest->status().good()) { _valid = true; - _originalPixmap = _ongoingRequest->scaledThumbnail(_requestedSize); - _scaledPixmaps.clear(); + _originalImage = + _ongoingRequest->scaledThumbnail(_requestedSize); + _scaledImages.clear(); for (auto n: notifiers) n(); } @@ -86,21 +92,22 @@ QPixmap Avatar::Private::get(int width, int height, Avatar::notifier_t notifier) }); } - if( _originalPixmap.isNull() ) + if( _originalImage.isNull() ) { if (_defaultIcon.isNull()) - return _originalPixmap; + return _originalImage; - _originalPixmap = _defaultIcon.pixmap(size); + QPainter p { &_originalImage }; + _defaultIcon.paint(&p, { QPoint(), _defaultIcon.actualSize(size) }); } - for (auto p: _scaledPixmaps) + for (auto p: _scaledImages) if (p.first == size) return p.second; - auto pixmap = _originalPixmap.scaled(size, + auto result = _originalImage.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - _scaledPixmaps.emplace_back(size, pixmap); - return pixmap; + _scaledImages.emplace_back(size, result); + return result; } QUrl Avatar::url() const { return d->_url; } diff --git a/avatar.h b/avatar.h index e71fecd7..d8b4b206 100644 --- a/avatar.h +++ b/avatar.h @@ -36,7 +36,8 @@ namespace QMatrixClient using notifier_t = std::function; - QPixmap get(int w, int h, notifier_t notifier); + QImage get(int dimension, notifier_t notifier); + QImage get(int w, int h, notifier_t notifier); QUrl url() const; bool updateUrl(const QUrl& newUrl); diff --git a/jobs/mediathumbnailjob.cpp b/jobs/mediathumbnailjob.cpp index 5945493a..c0d67a63 100644 --- a/jobs/mediathumbnailjob.cpp +++ b/jobs/mediathumbnailjob.cpp @@ -36,19 +36,20 @@ MediaThumbnailJob::MediaThumbnailJob(QUrl url, QSize requestedSize, })) { } -QPixmap MediaThumbnailJob::thumbnail() const +QImage MediaThumbnailJob::thumbnail() const { - return pixmap; + return _thumbnail; } -QPixmap MediaThumbnailJob::scaledThumbnail(QSize toSize) const +QImage MediaThumbnailJob::scaledThumbnail(QSize toSize) const { - return pixmap.scaled(toSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + return _thumbnail.scaled(toSize, + Qt::KeepAspectRatio, Qt::SmoothTransformation); } BaseJob::Status MediaThumbnailJob::parseReply(QByteArray data) { - if( !pixmap.loadFromData(data) ) + if( !_thumbnail.loadFromData(data) ) { qCDebug(JOBS) << "MediaThumbnailJob: could not read image data"; } diff --git a/jobs/mediathumbnailjob.h b/jobs/mediathumbnailjob.h index 292e7f14..f8f36fe9 100644 --- a/jobs/mediathumbnailjob.h +++ b/jobs/mediathumbnailjob.h @@ -32,13 +32,13 @@ namespace QMatrixClient MediaThumbnailJob(QUrl url, QSize requestedSize, ThumbnailType thumbnailType = ThumbnailType::Scale); - QPixmap thumbnail() const; - QPixmap scaledThumbnail(QSize toSize) const; + QImage thumbnail() const; + QImage scaledThumbnail(QSize toSize) const; protected: Status parseReply(QByteArray data) override; private: - QPixmap pixmap; + QImage _thumbnail; }; } // namespace QMatrixClient diff --git a/room.cpp b/room.cpp index 0024683d..1e159fd5 100644 --- a/room.cpp +++ b/room.cpp @@ -214,7 +214,12 @@ QString Room::topic() const return d->topic; } -QPixmap Room::avatar(int width, int height) +QImage Room::avatar(int dimension) +{ + return avatar(dimension, dimension); +} + +QImage Room::avatar(int width, int height) { if (!d->avatar.url().isEmpty()) return d->avatar.get(width, height, [=] { emit avatarChanged(); }); diff --git a/room.h b/room.h index 9a458c4e..f863d41b 100644 --- a/room.h +++ b/room.h @@ -108,11 +108,20 @@ namespace QMatrixClient Q_INVOKABLE int timelineSize() const; /** - * Returns a room avatar and requests it from the network if needed + * Returns a square room avatar with the given size and requests it + * from the network if needed * @return a pixmap with the avatar or a placeholder if there's none * available yet */ - Q_INVOKABLE QPixmap avatar(int width, int height); + Q_INVOKABLE QImage avatar(int dimension); + /** + * Returns a room avatar with the given dimensions and requests it + * from the network if needed + * @return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QImage avatar(int width, int height); + /** * @brief Produces a disambiguated name for a given user in * the context of the room diff --git a/user.cpp b/user.cpp index faad6231..6d2a2030 100644 --- a/user.cpp +++ b/user.cpp @@ -95,7 +95,12 @@ Avatar& User::avatarObject() return d->avatar; } -QPixmap User::avatar(int width, int height) +QImage User::avatar(int dimension) +{ + return avatar(dimension, dimension); +} + +QImage User::avatar(int width, int height) { return d->avatar.get(width, height, [=] { emit avatarChanged(this); }); } diff --git a/user.h b/user.h index 148ed64d..b7d67fb2 100644 --- a/user.h +++ b/user.h @@ -54,7 +54,8 @@ namespace QMatrixClient Q_INVOKABLE QString bridged() const; Avatar& avatarObject(); - QPixmap avatar(int requestedWidth, int requestedHeight); + QImage avatar(int dimension); + QImage avatar(int requestedWidth, int requestedHeight); QUrl avatarUrl() const; -- cgit v1.2.3 From 206834e8dc637287baf7e631065de6d951e704bf Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 28 Dec 2017 16:28:41 +0900 Subject: NetworkSettings: add setupApplicationProxy() accessible from QML; proxyPort is quint16 now setupApplicationProxy(), so that clients in QML could apply changes in proxy settings without going down to C++. quint16, because this is a type actually used by QNetworkProxy. --- networksettings.cpp | 10 ++++++++-- networksettings.h | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/networksettings.cpp b/networksettings.cpp index 6b023bd5..48bd09f3 100644 --- a/networksettings.cpp +++ b/networksettings.cpp @@ -20,6 +20,12 @@ using namespace QMatrixClient; -QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::NoProxy, setProxyType) +void NetworkSettings::setupApplicationProxy() const +{ + QNetworkProxy::setApplicationProxy( + { proxyType(), proxyHostName(), proxyPort() }); +} + +QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::DefaultProxy, setProxyType) QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", "", setProxyHostName) -QMC_DEFINE_SETTING(NetworkSettings, int, proxyPort, "proxy_port", -1, setProxyPort) +QMC_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, setProxyPort) diff --git a/networksettings.h b/networksettings.h index e113cd9b..83613060 100644 --- a/networksettings.h +++ b/networksettings.h @@ -30,7 +30,7 @@ namespace QMatrixClient { Q_OBJECT QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) - QMC_DECLARE_SETTING(int, proxyPort, setProxyPort) + QMC_DECLARE_SETTING(quint16, proxyPort, setProxyPort) Q_PROPERTY(QString proxyHost READ proxyHostName WRITE setProxyHostName) public: template @@ -38,5 +38,7 @@ namespace QMatrixClient { : SettingsGroup(QStringLiteral("Network"), qsettingsArgs...) { } ~NetworkSettings() override = default; + + Q_INVOKABLE void setupApplicationProxy() const; }; } -- cgit v1.2.3 From 1587c356229b293eeb0b810f57a902a953381267 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Dec 2017 20:31:52 +0900 Subject: Use std::unique_ptr instead of bare pointers; cleanup stale declarations --- connection.cpp | 10 ++++------ connection.h | 6 ++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/connection.cpp b/connection.cpp index 3083eeba..8765950a 100644 --- a/connection.cpp +++ b/connection.cpp @@ -44,15 +44,14 @@ using namespace QMatrixClient; class Connection::Private { public: - explicit Private(const QUrl& serverUrl) - : q(nullptr) - , data(new ConnectionData(serverUrl)) + explicit Private(std::unique_ptr&& connection) + : data(move(connection)) { } Q_DISABLE_COPY(Private) Private(Private&&) = delete; Private operator=(Private&&) = delete; - Connection* q; + Connection* q = nullptr; std::unique_ptr data; // A complex key below is a pair of room name and whether its // state is Invited. The spec mandates to keep Invited room state @@ -72,7 +71,7 @@ class Connection::Private Connection::Connection(const QUrl& server, QObject* parent) : QObject(parent) - , d(new Private(server)) + , d(std::make_unique(std::make_unique(server))) { d->q = this; // All d initialization should occur before this line } @@ -85,7 +84,6 @@ Connection::~Connection() { qCDebug(MAIN) << "deconstructing connection object for" << d->userId; stopSync(); - delete d; } void Connection::resolveServer(const QString& mxidOrDomain) diff --git a/connection.h b/connection.h index 21fce741..8dda2bbf 100644 --- a/connection.h +++ b/connection.h @@ -26,15 +26,13 @@ #include #include - -class QDnsLookup; +#include namespace QMatrixClient { class Room; class User; class RoomEvent; - class ConnectionPrivate; class ConnectionData; class SyncJob; @@ -307,7 +305,7 @@ namespace QMatrixClient private: class Private; - Private* d; + std::unique_ptr d; /** * A single entry for functions that need to check whether the -- cgit v1.2.3 From 06645269475561027790e0b58602647a92d6599d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Dec 2017 20:41:14 +0900 Subject: Allow to customize and connect to the used QNetworkAccessManager from clients We don't really want to let the world know that we use the only instance of NAM; instead, we provide a point of customisation to whatever NAM is created in ConnectionData, in the form of a static customizeNetworkAccess() method that gets a function to run on a/the created NAM. This function can do additional configuration on NAM (such as setting a proxy factory, network configuration, or caching) and/or connect to its signals, such as sslErrors() and proxyAuthenticationRequired(). Closes #143. --- connectiondata.cpp | 53 +++++++++++++++++++++++++++++++---------------------- connectiondata.h | 8 +++++++- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/connectiondata.cpp b/connectiondata.cpp index 70791952..4bfddd00 100644 --- a/connectiondata.cpp +++ b/connectiondata.cpp @@ -24,20 +24,10 @@ using namespace QMatrixClient; -QNetworkAccessManager* createNam() -{ - auto nam = new QNetworkAccessManager(); - // See #109. Once Qt bearer management gets better, this workaround - // should become unnecessary. - nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, - nam, [=] { - nam->setNetworkAccessible(QNetworkAccessManager::Accessible); - }); - return nam; -} - struct ConnectionData::Private { + explicit Private(const QUrl& url) : baseUrl(url) { } + QUrl baseUrl; QByteArray accessToken; QString lastEvent; @@ -45,19 +35,19 @@ struct ConnectionData::Private mutable unsigned int txnCounter = 0; const qint64 id = QDateTime::currentMSecsSinceEpoch(); + + static QNetworkAccessManager* createNam(); + static nam_customizer_t customizeNam; }; +ConnectionData::nam_customizer_t ConnectionData::Private::customizeNam = + [] (auto* /* unused */) { }; + ConnectionData::ConnectionData(QUrl baseUrl) - : d(new Private) -{ - nam(); // Just to ensure NAM is created - d->baseUrl = baseUrl; -} + : d(std::make_unique(baseUrl)) +{ } -ConnectionData::~ConnectionData() -{ - delete d; -} +ConnectionData::~ConnectionData() = default; QByteArray ConnectionData::accessToken() const { @@ -69,9 +59,21 @@ QUrl ConnectionData::baseUrl() const return d->baseUrl; } +QNetworkAccessManager* ConnectionData::Private::createNam() +{ + auto nam = new QNetworkAccessManager; + // See #109. Once Qt bearer management gets better, this workaround + // should become unnecessary. + nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, + [nam] { nam->setNetworkAccessible(QNetworkAccessManager::Accessible); + }); + customizeNam(nam); + return nam; +} + QNetworkAccessManager* ConnectionData::nam() const { - static auto nam = createNam(); + static auto nam = d->createNam(); return nam; } @@ -124,3 +126,10 @@ QByteArray ConnectionData::generateTxnId() const return QByteArray::number(d->id) + 'q' + QByteArray::number(++d->txnCounter); } + +void +ConnectionData::customizeNetworkAccess(ConnectionData::nam_customizer_t customizer) +{ + Private::customizeNam = customizer; +} + diff --git a/connectiondata.h b/connectiondata.h index 530a52ee..9cdc32ce 100644 --- a/connectiondata.h +++ b/connectiondata.h @@ -20,6 +20,8 @@ #include +#include + class QNetworkAccessManager; namespace QMatrixClient @@ -46,8 +48,12 @@ namespace QMatrixClient QByteArray generateTxnId() const; + using nam_customizer_t = + std::function; + static void customizeNetworkAccess(nam_customizer_t customizer); + private: struct Private; - Private* d; + std::unique_ptr d; }; } // namespace QMatrixClient -- cgit v1.2.3 From 9790e94cd6e490d58823b8782af0302d23a7c3bb Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Dec 2017 22:58:44 +0900 Subject: Fix building with Microsoft's standard library --- connectiondata.h | 1 + 1 file changed, 1 insertion(+) diff --git a/connectiondata.h b/connectiondata.h index 9cdc32ce..9f17a9b5 100644 --- a/connectiondata.h +++ b/connectiondata.h @@ -20,6 +20,7 @@ #include +#include #include class QNetworkAccessManager; -- cgit v1.2.3 From e2147ca36d22194582f76faf8b6fc89623172084 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Dec 2017 23:23:32 +0900 Subject: Don't use generic lambdas in initialisers This seems to upset MSVC. --- connectiondata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connectiondata.cpp b/connectiondata.cpp index 4bfddd00..11015dbf 100644 --- a/connectiondata.cpp +++ b/connectiondata.cpp @@ -41,7 +41,7 @@ struct ConnectionData::Private }; ConnectionData::nam_customizer_t ConnectionData::Private::customizeNam = - [] (auto* /* unused */) { }; + [] (QNetworkAccessManager* /* unused */) { }; ConnectionData::ConnectionData(QUrl baseUrl) : d(std::make_unique(baseUrl)) -- cgit v1.2.3 From 4377a868f0d69ca92afca13b0087aee2d97bd7ac Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 30 Dec 2017 17:48:25 +0900 Subject: QMatrixClient::NetworkAccessManager (singleton Qt NAM that remembers ignored SSL errors) Closes #145. --- CMakeLists.txt | 1 + connectiondata.cpp | 31 ++------------------ connectiondata.h | 5 ---- libqmatrixclient.pri | 6 ++-- networkaccessmanager.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ networkaccessmanager.h | 49 ++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 36 deletions(-) create mode 100644 networkaccessmanager.cpp create mode 100644 networkaccessmanager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fea5b0c..463bfea7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ message( STATUS ) # Set up source files set(libqmatrixclient_SRCS + networkaccessmanager.cpp connectiondata.cpp connection.cpp logging.cpp diff --git a/connectiondata.cpp b/connectiondata.cpp index 11015dbf..4e9bc77e 100644 --- a/connectiondata.cpp +++ b/connectiondata.cpp @@ -18,10 +18,9 @@ #include "connectiondata.h" +#include "networkaccessmanager.h" #include "logging.h" -#include - using namespace QMatrixClient; struct ConnectionData::Private @@ -35,14 +34,8 @@ struct ConnectionData::Private mutable unsigned int txnCounter = 0; const qint64 id = QDateTime::currentMSecsSinceEpoch(); - - static QNetworkAccessManager* createNam(); - static nam_customizer_t customizeNam; }; -ConnectionData::nam_customizer_t ConnectionData::Private::customizeNam = - [] (QNetworkAccessManager* /* unused */) { }; - ConnectionData::ConnectionData(QUrl baseUrl) : d(std::make_unique(baseUrl)) { } @@ -59,22 +52,9 @@ QUrl ConnectionData::baseUrl() const return d->baseUrl; } -QNetworkAccessManager* ConnectionData::Private::createNam() -{ - auto nam = new QNetworkAccessManager; - // See #109. Once Qt bearer management gets better, this workaround - // should become unnecessary. - nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, - [nam] { nam->setNetworkAccessible(QNetworkAccessManager::Accessible); - }); - customizeNam(nam); - return nam; -} - QNetworkAccessManager* ConnectionData::nam() const { - static auto nam = d->createNam(); - return nam; + return NetworkAccessManager::instance(); } void ConnectionData::setBaseUrl(QUrl baseUrl) @@ -126,10 +106,3 @@ QByteArray ConnectionData::generateTxnId() const return QByteArray::number(d->id) + 'q' + QByteArray::number(++d->txnCounter); } - -void -ConnectionData::customizeNetworkAccess(ConnectionData::nam_customizer_t customizer) -{ - Private::customizeNam = customizer; -} - diff --git a/connectiondata.h b/connectiondata.h index 9f17a9b5..7a2f2e90 100644 --- a/connectiondata.h +++ b/connectiondata.h @@ -20,7 +20,6 @@ #include -#include #include class QNetworkAccessManager; @@ -49,10 +48,6 @@ namespace QMatrixClient QByteArray generateTxnId() const; - using nam_customizer_t = - std::function; - static void customizeNetworkAccess(nam_customizer_t customizer); - private: struct Private; std::unique_ptr d; diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 5910554c..8ee3634c 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -33,7 +33,8 @@ HEADERS += \ $$files($$PWD/jobs/generated/*.h, false) \ $$PWD/logging.h \ $$PWD/settings.h \ - $$PWD/networksettings.h + $$PWD/networksettings.h \ + $$PWD/networkaccessmanager.h SOURCES += \ $$PWD/connectiondata.cpp \ @@ -61,4 +62,5 @@ SOURCES += \ $$files($$PWD/jobs/generated/*.cpp, false) \ $$PWD/logging.cpp \ $$PWD/settings.cpp \ - $$PWD/networksettings.cpp + $$PWD/networksettings.cpp \ + $$PWD/networkaccessmanager.cpp diff --git a/networkaccessmanager.cpp b/networkaccessmanager.cpp new file mode 100644 index 00000000..7fb2f602 --- /dev/null +++ b/networkaccessmanager.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + * Copyright (C) 2018 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "networkaccessmanager.h" + +#include + +using namespace QMatrixClient; + +class NetworkAccessManager::Private +{ + public: + QList ignoredSslErrors; +}; + +NetworkAccessManager::NetworkAccessManager() : d(std::make_unique()) +{ } + +QList NetworkAccessManager::ignoredSslErrors() const +{ + return d->ignoredSslErrors; +} + +void NetworkAccessManager::addIgnoredSslError(const QSslError& error) +{ + d->ignoredSslErrors << error; +} + +void NetworkAccessManager::clearIgnoredSslErrors() +{ + d->ignoredSslErrors.clear(); +} + +static NetworkAccessManager* createNam() +{ + auto nam = new NetworkAccessManager; + // See #109. Once Qt bearer management gets better, this workaround + // should become unnecessary. + nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, + [nam] { nam->setNetworkAccessible(QNetworkAccessManager::Accessible); }); + return nam; +} + +NetworkAccessManager*NetworkAccessManager::instance() +{ + static auto* nam = createNam(); + return nam; +} + +NetworkAccessManager::~NetworkAccessManager() = default; + +QNetworkReply* NetworkAccessManager::createRequest(Operation op, + const QNetworkRequest& request, QIODevice* outgoingData) +{ + auto reply = + QNetworkAccessManager::createRequest(op, request, outgoingData); + reply->ignoreSslErrors(d->ignoredSslErrors); + return reply; +} diff --git a/networkaccessmanager.h b/networkaccessmanager.h new file mode 100644 index 00000000..ea08c591 --- /dev/null +++ b/networkaccessmanager.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Copyright (C) 2018 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include + +#include + +namespace QMatrixClient +{ + class NetworkAccessManager : public QNetworkAccessManager + { + Q_OBJECT + public: + NetworkAccessManager(); + ~NetworkAccessManager() override; + + QList ignoredSslErrors() const; + void addIgnoredSslError(const QSslError& error); + void clearIgnoredSslErrors(); + + /** Get a pointer to the singleton */ + static NetworkAccessManager* instance(); + + private: + QNetworkReply * createRequest(Operation op, + const QNetworkRequest &request, + QIODevice *outgoingData = Q_NULLPTR) override; + + class Private; + std::unique_ptr d; + }; +} // namespace QMatrixClient -- cgit v1.2.3 From 2c440249052b0d518fccd953a7dc657f9eed7ab7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 30 Dec 2017 17:50:07 +0900 Subject: BaseJob: do not suppress SSL errors It is the application's responsibility to properly display the error and get confirmation from the user about it. --- jobs/basejob.cpp | 9 --------- jobs/basejob.h | 1 - 2 files changed, 10 deletions(-) diff --git a/jobs/basejob.cpp b/jobs/basejob.cpp index 3ea13c83..8d116c26 100644 --- a/jobs/basejob.cpp +++ b/jobs/basejob.cpp @@ -191,7 +191,6 @@ void BaseJob::sendRequest() if (!d->requestQuery.isEmpty()) qCDebug(d->logCat) << " query:" << d->requestQuery.toString(); d->sendRequest(); - connect( d->reply.data(), &QNetworkReply::sslErrors, this, &BaseJob::sslErrors ); connect( d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply ); if (d->reply->isRunning()) { @@ -371,14 +370,6 @@ void BaseJob::timeout() finishJob(); } -void BaseJob::sslErrors(const QList& errors) -{ - foreach (const QSslError &error, errors) { - qCWarning(d->logCat) << "SSL ERROR" << error.errorString(); - } - d->reply->ignoreSslErrors(); // TODO: insecure! should prompt user first -} - void BaseJob::setLoggingCategory(LoggingCategory lcf) { d->logCat = lcf; diff --git a/jobs/basejob.h b/jobs/basejob.h index 0d669b78..e3a379fa 100644 --- a/jobs/basejob.h +++ b/jobs/basejob.h @@ -267,7 +267,6 @@ namespace QMatrixClient virtual ~BaseJob(); protected slots: void timeout(); - void sslErrors(const QList& errors); private slots: void sendRequest(); -- cgit v1.2.3