diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-04-13 12:36:03 +0900 |
---|---|---|
committer | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-04-13 14:44:04 +0900 |
commit | b68186efc5fcedad6980686c8a07bab3b47a874d (patch) | |
tree | 1aaf5de974996279ec7e82af199d65fc5a187a48 | |
parent | 44764f015f25db307811f2969c117b37133fc676 (diff) | |
parent | 7ca442a432994a19a86d43c917a1f537bcebb387 (diff) | |
download | libquotient-b68186efc5fcedad6980686c8a07bab3b47a874d.tar.gz libquotient-b68186efc5fcedad6980686c8a07bab3b47a874d.zip |
Merge branch 'master' into kitsune-gtad
-rw-r--r-- | .travis.yml | 14 | ||||
-rw-r--r-- | CMakeLists.txt | 141 | ||||
-rw-r--r-- | CONTRIBUTING.md | 32 | ||||
-rw-r--r-- | README.md | 40 | ||||
-rw-r--r-- | cmake/QMatrixClientConfig.cmake | 1 | ||||
-rw-r--r-- | examples/CMakeLists.txt | 69 | ||||
-rw-r--r-- | examples/qmc-example.cpp | 20 | ||||
-rw-r--r-- | lib/avatar.cpp (renamed from avatar.cpp) | 0 | ||||
-rw-r--r-- | lib/avatar.h (renamed from avatar.h) | 0 | ||||
-rw-r--r-- | lib/connection.cpp (renamed from connection.cpp) | 124 | ||||
-rw-r--r-- | lib/connection.h (renamed from connection.h) | 28 | ||||
-rw-r--r-- | lib/connectiondata.cpp (renamed from connectiondata.cpp) | 0 | ||||
-rw-r--r-- | lib/connectiondata.h (renamed from connectiondata.h) | 0 | ||||
-rw-r--r-- | lib/converters.h (renamed from converters.h) | 0 | ||||
-rw-r--r-- | lib/events/accountdataevents.h (renamed from events/accountdataevents.h) | 0 | ||||
-rw-r--r-- | lib/events/directchatevent.cpp (renamed from events/directchatevent.cpp) | 11 | ||||
-rw-r--r-- | lib/events/directchatevent.h (renamed from events/directchatevent.h) | 0 | ||||
-rw-r--r-- | lib/events/event.cpp (renamed from events/event.cpp) | 0 | ||||
-rw-r--r-- | lib/events/event.h (renamed from events/event.h) | 2 | ||||
-rw-r--r-- | lib/events/eventcontent.cpp (renamed from events/eventcontent.cpp) | 0 | ||||
-rw-r--r-- | lib/events/eventcontent.h (renamed from events/eventcontent.h) | 0 | ||||
-rw-r--r-- | lib/events/receiptevent.cpp (renamed from events/receiptevent.cpp) | 0 | ||||
-rw-r--r-- | lib/events/receiptevent.h (renamed from events/receiptevent.h) | 0 | ||||
-rw-r--r-- | lib/events/redactionevent.cpp (renamed from events/redactionevent.cpp) | 0 | ||||
-rw-r--r-- | lib/events/redactionevent.h (renamed from events/redactionevent.h) | 0 | ||||
-rw-r--r-- | lib/events/roomavatarevent.cpp (renamed from events/roomavatarevent.cpp) | 0 | ||||
-rw-r--r-- | lib/events/roomavatarevent.h (renamed from events/roomavatarevent.h) | 0 | ||||
-rw-r--r-- | lib/events/roommemberevent.cpp (renamed from events/roommemberevent.cpp) | 0 | ||||
-rw-r--r-- | lib/events/roommemberevent.h (renamed from events/roommemberevent.h) | 0 | ||||
-rw-r--r-- | lib/events/roommessageevent.cpp (renamed from events/roommessageevent.cpp) | 2 | ||||
-rw-r--r-- | lib/events/roommessageevent.h (renamed from events/roommessageevent.h) | 0 | ||||
-rw-r--r-- | lib/events/simplestateevents.h (renamed from events/simplestateevents.h) | 0 | ||||
-rw-r--r-- | lib/events/typingevent.cpp (renamed from events/typingevent.cpp) | 0 | ||||
-rw-r--r-- | lib/events/typingevent.h (renamed from events/typingevent.h) | 0 | ||||
-rw-r--r-- | lib/jobs/basejob.cpp (renamed from jobs/basejob.cpp) | 26 | ||||
-rw-r--r-- | lib/jobs/basejob.h (renamed from jobs/basejob.h) | 7 | ||||
-rw-r--r-- | lib/jobs/checkauthmethods.cpp (renamed from jobs/checkauthmethods.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/checkauthmethods.h (renamed from jobs/checkauthmethods.h) | 0 | ||||
-rw-r--r-- | lib/jobs/downloadfilejob.cpp (renamed from jobs/downloadfilejob.cpp) | 20 | ||||
-rw-r--r-- | lib/jobs/downloadfilejob.h (renamed from jobs/downloadfilejob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/account-data.cpp (renamed from jobs/generated/account-data.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/account-data.h (renamed from jobs/generated/account-data.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/administrative_contact.cpp (renamed from jobs/generated/administrative_contact.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/administrative_contact.h (renamed from jobs/generated/administrative_contact.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/banning.cpp (renamed from jobs/generated/banning.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/banning.h (renamed from jobs/generated/banning.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/content-repo.cpp (renamed from jobs/generated/content-repo.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/content-repo.h (renamed from jobs/generated/content-repo.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/create_room.cpp (renamed from jobs/generated/create_room.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/create_room.h (renamed from jobs/generated/create_room.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/directory.cpp (renamed from jobs/generated/directory.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/directory.h (renamed from jobs/generated/directory.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/inviting.cpp (renamed from jobs/generated/inviting.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/inviting.h (renamed from jobs/generated/inviting.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/kicking.cpp (renamed from jobs/generated/kicking.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/kicking.h (renamed from jobs/generated/kicking.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/leaving.cpp (renamed from jobs/generated/leaving.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/leaving.h (renamed from jobs/generated/leaving.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/list_joined_rooms.cpp (renamed from jobs/generated/list_joined_rooms.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/list_joined_rooms.h (renamed from jobs/generated/list_joined_rooms.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/list_public_rooms.cpp (renamed from jobs/generated/list_public_rooms.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/list_public_rooms.h (renamed from jobs/generated/list_public_rooms.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/login.cpp (renamed from jobs/generated/login.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/login.h (renamed from jobs/generated/login.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/logout.cpp (renamed from jobs/generated/logout.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/logout.h (renamed from jobs/generated/logout.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/profile.cpp (renamed from jobs/generated/profile.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/profile.h (renamed from jobs/generated/profile.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/pusher.cpp (renamed from jobs/generated/pusher.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/pusher.h (renamed from jobs/generated/pusher.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/receipts.cpp (renamed from jobs/generated/receipts.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/receipts.h (renamed from jobs/generated/receipts.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/redaction.cpp (renamed from jobs/generated/redaction.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/redaction.h (renamed from jobs/generated/redaction.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/room_send.cpp (renamed from jobs/generated/room_send.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/room_send.h (renamed from jobs/generated/room_send.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/tags.cpp (renamed from jobs/generated/tags.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/tags.h (renamed from jobs/generated/tags.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/third_party_membership.cpp (renamed from jobs/generated/third_party_membership.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/third_party_membership.h (renamed from jobs/generated/third_party_membership.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/to_device.cpp (renamed from jobs/generated/to_device.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/to_device.h (renamed from jobs/generated/to_device.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/typing.cpp (renamed from jobs/generated/typing.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/typing.h (renamed from jobs/generated/typing.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/users.cpp (renamed from jobs/generated/users.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/users.h (renamed from jobs/generated/users.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/versions.cpp (renamed from jobs/generated/versions.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/versions.h (renamed from jobs/generated/versions.h) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/whoami.cpp (renamed from jobs/generated/whoami.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/generated/whoami.h (renamed from jobs/generated/whoami.h) | 0 | ||||
-rw-r--r-- | lib/jobs/gtad.yaml (renamed from jobs/gtad.yaml) | 0 | ||||
-rw-r--r-- | lib/jobs/joinroomjob.cpp (renamed from jobs/joinroomjob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/joinroomjob.h (renamed from jobs/joinroomjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/mediathumbnailjob.cpp (renamed from jobs/mediathumbnailjob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/mediathumbnailjob.h (renamed from jobs/mediathumbnailjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/passwordlogin.cpp (renamed from jobs/passwordlogin.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/passwordlogin.h (renamed from jobs/passwordlogin.h) | 0 | ||||
-rw-r--r-- | lib/jobs/postreadmarkersjob.h (renamed from jobs/postreadmarkersjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/postreceiptjob.cpp (renamed from jobs/postreceiptjob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/postreceiptjob.h (renamed from jobs/postreceiptjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/preamble.mustache (renamed from jobs/preamble.mustache) | 0 | ||||
-rw-r--r-- | lib/jobs/requestdata.cpp (renamed from jobs/requestdata.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/requestdata.h (renamed from jobs/requestdata.h) | 0 | ||||
-rw-r--r-- | lib/jobs/roommessagesjob.cpp (renamed from jobs/roommessagesjob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/roommessagesjob.h (renamed from jobs/roommessagesjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/sendeventjob.cpp (renamed from jobs/sendeventjob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/sendeventjob.h (renamed from jobs/sendeventjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/setroomstatejob.cpp (renamed from jobs/setroomstatejob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/setroomstatejob.h (renamed from jobs/setroomstatejob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/syncjob.cpp (renamed from jobs/syncjob.cpp) | 0 | ||||
-rw-r--r-- | lib/jobs/syncjob.h (renamed from jobs/syncjob.h) | 0 | ||||
-rw-r--r-- | lib/jobs/{{base}}.cpp.mustache (renamed from jobs/{{base}}.cpp.mustache) | 0 | ||||
-rw-r--r-- | lib/jobs/{{base}}.h.mustache (renamed from jobs/{{base}}.h.mustache) | 0 | ||||
-rw-r--r-- | lib/joinstate.h (renamed from joinstate.h) | 0 | ||||
-rw-r--r-- | lib/logging.cpp (renamed from logging.cpp) | 0 | ||||
-rw-r--r-- | lib/logging.h (renamed from logging.h) | 0 | ||||
-rw-r--r-- | lib/networkaccessmanager.cpp (renamed from networkaccessmanager.cpp) | 0 | ||||
-rw-r--r-- | lib/networkaccessmanager.h (renamed from networkaccessmanager.h) | 0 | ||||
-rw-r--r-- | lib/networksettings.cpp (renamed from networksettings.cpp) | 0 | ||||
-rw-r--r-- | lib/networksettings.h (renamed from networksettings.h) | 0 | ||||
-rw-r--r-- | lib/room.cpp (renamed from room.cpp) | 11 | ||||
-rw-r--r-- | lib/room.h (renamed from room.h) | 13 | ||||
-rw-r--r-- | lib/settings.cpp (renamed from settings.cpp) | 246 | ||||
-rw-r--r-- | lib/settings.h (renamed from settings.h) | 0 | ||||
-rw-r--r-- | lib/user.cpp (renamed from user.cpp) | 1 | ||||
-rw-r--r-- | lib/user.h (renamed from user.h) | 0 | ||||
-rw-r--r-- | lib/util.h | 59 | ||||
-rw-r--r-- | libqmatrixclient.pri | 133 | ||||
-rw-r--r-- | state.cpp | 69 | ||||
-rw-r--r-- | state.h | 47 | ||||
-rw-r--r-- | util.h | 205 |
131 files changed, 669 insertions, 652 deletions
diff --git a/.travis.yml b/.travis.yml index 9b690d10..531eec3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,15 +33,23 @@ install: - popd before_script: -- mkdir build && cd build -- cmake -DMATRIX_DOC_PATH="matrix-doc" -DGTAD_PATH="gtad/gtad" -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} .. +- mkdir build && pushd build +- cmake -DMATRIX_DOC_PATH="matrix-doc" -DGTAD_PATH="gtad/gtad" -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_PREFIX=../install .. - cmake --build . --target update-api +- popd script: +- cmake --build build --target all +- cmake --build build --target install +# Build qmc-example with the installed library +- mkdir build-example && pushd build-example +- cmake -DCMAKE_PREFIX_PATH=../install ../examples - cmake --build . --target all -- cd .. +- popd +# Build and install with qmake - qmake qmc-example.pro "CONFIG += debug" "CONFIG -= app_bundle" "QMAKE_CC = $CC" "QMAKE_CXX = $CXX" - make all +# Run the qmake-compiled qmc-example under valgrind - if [ "$QMC_TEST_USER" != "" ]; then $VALGRIND ./qmc-example "$QMC_TEST_USER" "$QMC_TEST_PWD" qmc-example-travis '#qmc-test:matrix.org' "Travis CI job $TRAVIS_JOB_NUMBER"; fi notifications: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e5b191e..29171e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,10 @@ cmake_minimum_required(VERSION 3.1) project(qmatrixclient CXX) include(CheckCXXCompilerFlag) +if (NOT WIN32) + include(GNUInstallDirs) +endif(NOT WIN32) -# Find includes in corresponding build directories -set(CMAKE_INCLUDE_CURRENT_DIR ON) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) @@ -18,6 +19,18 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) "MinSizeRel" "RelWithDebInfo") endif() +if (NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR ".") +endif() + +if (NOT CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR ".") +endif() + +if (NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "include") +endif() + set(CMAKE_CXX_STANDARD 14) foreach (FLAG all "" pedantic extra error=return-type no-unused-parameter no-gnu-zero-variadic-macro-arguments) @@ -27,7 +40,7 @@ foreach (FLAG all "" pedantic extra error=return-type no-unused-parameter no-gnu endif () endforeach () -find_package(Qt5 5.6 REQUIRED Network Gui) +find_package(Qt5 5.5.1 REQUIRED Network Gui) get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) message( STATUS ) @@ -49,33 +62,33 @@ message( STATUS ) # Set up source files set(libqmatrixclient_SRCS - networkaccessmanager.cpp - connectiondata.cpp - connection.cpp - logging.cpp - room.cpp - user.cpp - avatar.cpp - settings.cpp - networksettings.cpp - events/event.cpp - events/eventcontent.cpp - events/roommessageevent.cpp - events/roommemberevent.cpp - events/roomavatarevent.cpp - events/typingevent.cpp - events/receiptevent.cpp - events/directchatevent.cpp - jobs/requestdata.cpp - jobs/basejob.cpp - jobs/checkauthmethods.cpp - jobs/sendeventjob.cpp - jobs/setroomstatejob.cpp - jobs/joinroomjob.cpp - jobs/roommessagesjob.cpp - jobs/syncjob.cpp - jobs/mediathumbnailjob.cpp - jobs/downloadfilejob.cpp + lib/networkaccessmanager.cpp + lib/connectiondata.cpp + lib/connection.cpp + lib/logging.cpp + lib/room.cpp + lib/user.cpp + lib/avatar.cpp + lib/settings.cpp + lib/networksettings.cpp + lib/events/event.cpp + lib/events/eventcontent.cpp + lib/events/roommessageevent.cpp + lib/events/roommemberevent.cpp + lib/events/roomavatarevent.cpp + lib/events/typingevent.cpp + lib/events/receiptevent.cpp + lib/events/directchatevent.cpp + lib/jobs/requestdata.cpp + lib/jobs/basejob.cpp + lib/jobs/checkauthmethods.cpp + lib/jobs/sendeventjob.cpp + lib/jobs/setroomstatejob.cpp + lib/jobs/joinroomjob.cpp + lib/jobs/roommessagesjob.cpp + lib/jobs/syncjob.cpp + lib/jobs/mediathumbnailjob.cpp + lib/jobs/downloadfilejob.cpp ) set(API_DEF_PATH ${MATRIX_DOC_PATH}/api/client-server/) @@ -95,27 +108,73 @@ if (MATRIX_DOC_PATH AND GTAD_PATH) notifications.yaml- peeking_events.yaml- pushrules.yaml- rooms.yaml- search.yaml- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - SOURCES jobs/gtad.yaml - jobs/{{base}}.h.mustache jobs/{{base}}.cpp.mustache + SOURCES lib/jobs/gtad.yaml + lib/jobs/{{base}}.h.mustache lib/jobs/{{base}}.cpp.mustache ${API_DEFS} VERBATIM ) endif() -aux_source_directory(jobs/generated libqmatrixclient_job_SRCS) +aux_source_directory(lib/jobs/generated libqmatrixclient_job_SRCS) set(example_SRCS examples/qmc-example.cpp) -add_library(qmatrixclient ${libqmatrixclient_SRCS} ${libqmatrixclient_job_SRCS}) -set_property(TARGET qmatrixclient PROPERTY VERSION "0.2.0") -set_property(TARGET qmatrixclient PROPERTY SOVERSION 0 ) - -target_link_libraries(qmatrixclient Qt5::Core Qt5::Network Qt5::Gui) +add_library(QMatrixClient ${libqmatrixclient_SRCS} ${libqmatrixclient_job_SRCS}) +set(API_VERSION "0.2") +set_property(TARGET QMatrixClient PROPERTY VERSION "${API_VERSION}.0") +set_property(TARGET QMatrixClient PROPERTY SOVERSION 0 ) +set_property(TARGET QMatrixClient PROPERTY + INTERFACE_QMatrixClient_MAJOR_VERSION ${API_VERSION}) +set_property(TARGET QMatrixClient APPEND PROPERTY + COMPATIBLE_INTERFACE_STRING QMatrixClient_MAJOR_VERSION) + +target_include_directories(QMatrixClient PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> +) +target_link_libraries(QMatrixClient Qt5::Core Qt5::Network Qt5::Gui) add_executable(qmc-example ${example_SRCS}) -target_link_libraries(qmc-example Qt5::Core qmatrixclient) +target_link_libraries(qmc-example Qt5::Core QMatrixClient) + +# Installation + +install(TARGETS QMatrixClient EXPORT QMatrixClientTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +install(DIRECTORY lib/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h") + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient/QMatrixClientConfigVersion.cmake" + VERSION ${API_VERSION} + COMPATIBILITY AnyNewerVersion +) + +export(PACKAGE QMatrixClient) +export(EXPORT QMatrixClientTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient/QMatrixClientTargets.cmake") +configure_file(cmake/QMatrixClientConfig.cmake + "${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient/QMatrixClientConfig.cmake" + COPYONLY +) + +set(ConfigFilesLocation "${CMAKE_INSTALL_LIBDIR}/cmake/QMatrixClient") +install(EXPORT QMatrixClientTargets + FILE QMatrixClientTargets.cmake DESTINATION ${ConfigFilesLocation}) + +install(FILES cmake/QMatrixClientConfig.cmake + "${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient/QMatrixClientConfigVersion.cmake" + DESTINATION ${ConfigFilesLocation} +) +# Only available from CMake 3.7; reserved for future use +#install(EXPORT_ANDROID_MK QMatrixClientTargets DESTINATION share/ndk-modules) if (WIN32) - install (FILES mime/packages/freedesktop.org.xml - DESTINATION mime/packages) + install(FILES mime/packages/freedesktop.org.xml DESTINATION mime/packages) endif (WIN32) + +install(TARGETS qmc-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e576b886..6ad968ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ We use GitHub to track all changes via its [issue tracker](https://github.com/QMatrixClient/libqmatrixclient/issues) and [pull requests](https://github.com/QMatrixClient/libqmatrixclient/pulls). Specific changes are proposed using those mechanisms. -Issues are assigned to an individual, who works it and then marks it complete. +Issues are assigned to an individual who works on it and then marks it complete. If there are questions or objections, the conversation area of that issue or pull request is used to resolve it. @@ -126,25 +126,29 @@ This is our primary language. We don't have a particular code style _as of yet_ * Please don't make hypocritical structs with protected or private members. Just make them classes instead. * Qt containers are generally preferred to STL containers; however, there are notable exceptions, and libqmatrixclient already uses them: * `std::array` and `std::deque` have no direct counterparts in Qt. - * Because of COW semantics, Qt containers cannot hold uncopyable classes. Classes without a default constructor are a problem too. An example of that is `SyncRoomData` and `EventsBatch<>`. Use standard containers for those but see the next point and also consider if you can supply a reasonable copy/default constructor. - * Standard containers can be used in code internal to a translation unit (i.e., in a certain .cpp file) _as long as it's not exposed in the interface_. It's ok to use, e.g., `std::vector` instead of `QVector` in tighter code, or when dealing with uncopyable (see the previous point) elements. However, exposing standard containers in the API that might be used by QML will not work at all and will never be accepted. -* Use `QVector` instead of `QList` where possible - see a [great article of Marc Mutz on Qt containers](https://marcmutz.wordpress.com/effective-qt/containers/) for details. + * Because of COW semantics, Qt containers cannot hold uncopyable classes. Classes without a default constructor are a problem too. Examples of that are `SyncRoomData` and `EventsBatch<>`. Use STL containers for those but see the next point and also consider if you can supply a reasonable copy/default constructor. + * STL containers can be freely used in code internal to a translation unit (i.e., in a certain .cpp file) _as long as that is not exposed in the API_. It's ok to use, e.g., `std::vector` instead of `QVector` to tighten up code where you don't need COW, or when dealing with uncopyable data structures (see the previous point). However, exposing STL containers in the API is not encouraged (except where absolutely necessary, e.g. we use `std::deque` for a timeline). Exposing STL containers or iterators in API intended for usage by QML code (e.g. in `Q_PROPERTY`) will not work at all and will never be accepted. +* Use `QVector` instead of `QList` where possible - see a [great article by Marc Mutz on Qt containers](https://marcmutz.wordpress.com/effective-qt/containers/) for details. ### Automated tests -There's no testing framework as of now; either Catch or QTest or both will be used eventually. However, as a stopgap measure, qmc-example is used for end-to-end testing; so please add another private slot and call it from `QMCTest::doTests()` whenever you add new function worth it. PRs to set up a proper testing appliance instead are very welcome (make sure to migrate tests from qmc-example though). +There's no testing framework as of now; either Catch or Qt Test or both will be used eventually. However, as a stopgap measure, qmc-example is used for automated end-to-end testing. + +Any significant addition to the library API should be accompanied by a respective test in qmc-example. To add a test you should write a new private slot with the test logic to the `QMCTest` class and call it from `QMCTest::doTests()`. `QMCTest` sets up some basic test fixture to help you with testing; notably by the moment `doTests()` is invoked you can rely on having a working connection in `c` member variable and a test room in `targetRoom` member variable. PRs to introduce a proper testing framework are very welcome (make sure to migrate tests from qmc-example though); shifting qmc-example to use Qt Test seems to be a particularly low-hanging fruit. ### Security, privacy, and performance -Pay attention to security, and work *with* (not against) the usual security hardening mechanisms (however few in C++). `char *` and similar unchecked C-style read/write arrays are forbidden - use Qt containers or at the very least `std::array<>` instead. +Pay attention to security, and work *with* (not against) the usual security hardening mechanisms (however few in C++). + +`char *` and similar unchecked C-style read/write arrays are forbidden - use Qt containers or at the very least `std::array<>` instead. Where you see fit (usually with data structures), try to use smart pointers, especially `std::unique_ptr<>` or `QScopedPointer` instead of bare pointers. When dealing with `QObject`s, use the parent-child ownership semantics exercised by Qt. Shared pointers are not used in the code so far; but if you find a particular use case where strict semantics of unique pointers doesn't help and a shared pointer is necessary, feel free to step up with the working code and it will be considered for inclusion. Exercise the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) where reasonable and appropriate. Prefer less-coupled cohesive code. Protect private information, in particular passwords and email addresses. Do not forget about local access to data (in particular, be very careful when storing something in temporary files, let alone permanent configuration or state). Avoid mechanisms that could be used for tracking where possible (we do need to verify people are logged in but that's pretty much it), and ensure that third parties can't use interactions for tracking. -We want the software to have decent performance for typical users. At the same time we keep libqmatrixclient single-threaded as much as possible, to keep the code simple. That means being cautious about operation complexity (read about big-O notation if you need a kickstart on the topic). This especially refers to operations on the whole timeline - it can easily be several thousands elements long so even operations with linear complexity, if heavy enough, can produce noticeable GUI freezing. +We want the software to have decent performance for typical users. At the same time we keep libqmatrixclient single-threaded as much as possible, to keep the code simple. That means being cautious about operation complexity (read about big-O notation if you need a kickstart on the topic). This especially refers to operations on the whole timeline and the list of users - each of these can have tens of thousands of elements so even operations with linear complexity, if heavy enough, can produce noticeable GUI freezing. A solution to such cases is to embed `processEvents()` invocations in heavy loops (see `Connection::saveState()` to get the idea). -Having said that, there's always a trade-off between various attributes; in particular, readability and maintainability of the code is more important than squeezing every bit out of that clumsy algorithm. +Having said that, there's always a trade-off between various attributes; in particular, readability and maintainability of the code is more important than squeezing every bit out of that clumsy algorithm. Beware of premature optimization and have profiling information around if you are about to go into some hardcore optimization. ## Documentation changes @@ -166,11 +170,11 @@ Do not use trailing two spaces for line breaks, since these cannot be seen and m ## How to check proposed changes before submitting them -Checking the code on at least one configuration is essential; if you only have a hasty fix that doesn't even compile, better make an issue and put a link to your commit into it (with an explanation what it is about and why). +Checking the code on at least one configuration is essential; if you only have a hasty fix that doesn't even compile, better make an issue and put a link to your commit into it (with an explanation what it is about and why). The best check is a continuous integration pipeline automatically triggered when you submit a PR. ### Standard checks -`-Wall -pedantic` is used with GCC and Clang. We don't turn those warnings to errors but please treat them as such. +The following warnings configuration is applied with GCC and Clang when using CMake: `-W -Wall -Wextra -pedantic -Werror=return-type -Wno-unused-parameter -Wno-gnu-zero-variadic-macro-arguments` (the last one is to mute a warning triggered by Qt code for debug logging). We don't turn most of the warnings to errors but please treat them as such. In Qt Creator, the following line can be used with the Clang code model (you should explicitly enable the code model plugin): `-Weverything -Werror=return-type -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-macros -Wno-newline-eof -Wno-exit-time-destructors -Wno-global-constructors -Wno-gnu-zero-variadic-macro-arguments -Wno-documentation -Wno-missing-prototypes -Wno-shadow-field-in-constructor -Wno-padded -Wno-weak-vtables` ### Continuous Integration @@ -178,7 +182,7 @@ We use Travis CI to check buildability on Linux (GCC, Clang) and MacOS (Clang), ### Other tools -If you know how to use clang-tidy, here's a list of checks we do and do not use (a leading hyphen means a disabled check, an asterisk is a wildcard): `*,cert-env33-c,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-special-member-functions,-google-build-using-namespace,-google-readability-braces-around-statements,-hicpp-*,-llvm-*,-misc-unused-parameters,-misc-noexcept-moveconstructor,-modernize-use-using,-readability-braces-around-statements,readability-identifier-naming,-readability-implicit-bool-cast,-clang-diagnostic-*,-clang-analyzer-*`. If you're on CLion (which makes clang-tidy usage a no-brainer), you can simple copy-paste the above list into the Clang-Tidy inspection configuration. +If you know how to use clang-tidy, here's a list of checks we do and do not use (a leading hyphen means a disabled check, an asterisk is a wildcard): `*,cert-env33-c,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-special-member-functions,-google-build-using-namespace,-google-readability-braces-around-statements,-hicpp-*,-llvm-*,-misc-unused-parameters,-misc-noexcept-moveconstructor,-modernize-use-using,-readability-braces-around-statements,readability-identifier-naming,-readability-implicit-bool-cast,-clang-diagnostic-*,-clang-analyzer-*`. If you're on CLion, you can simply copy-paste the above list into the Clang-Tidy inspection configuration. In Qt Creator 4.6, one can enable clang-tidy and clazy (clazy level 1 eats away CPU but produces some very relevant and unobvious notices, such as possible unintended copying of a Qt container, or unguarded null pointers). ## Git commit messages @@ -196,13 +200,13 @@ When writing git commit messages, try to follow the guidelines in ## Reuse (libraries, frameworks, etc.) -C++ is unfortunately not very coherent about SDK/package management, and we try to keep building the library as easy as possible. Because of that we are very (and it means _very_) conservative about adding dependencies to libqmatrixclient. That relates to both additional Qt components and even more to other libraries. +C++ is unfortunately not very coherent about SDK/package management, and we try to keep building the library as easy as possible. Because of that we are very (and it means _very_) conservative about adding dependencies to libqmatrixclient. That relates to both additional Qt components and even more to other libraries. Fortunately, even the Qt components now in use (Qt Core and Network) are very feature-rich and provide plenty of ready-made stuff. -Regardless of the above paragraph (and as mentioned earlier in the text), we're now looking at possible options for automated testing, so PRs onboarding a test framework will be considered with much gratitude. Just don't forget to bootstrap with at least some tests on the existing codebase, rather than just throw in a Git submodule. +Regardless of the above paragraph (and as mentioned earlier in the text), we're now looking at possible options for automated testing, so PRs onboarding a test framework will be considered with much gratitude. Some cases need additional explanation: * Before rolling out your own super-optimised container or algorithm written from scratch, take a good long look through documentation on Qt and C++ standard library (including the experimental/future sections). Please try to reuse the existing facilities as much as possible. -* You should have a good reason (or better several ones) to add a component from KDE Frameworks. We don't rule this out and there's no prejudice against KDE; it just so happened that KDE Frameworks is one of most obvious reuse candidates for us but so far none of these components survived as libqmatrixclient deps. +* You should have a good reason (or better several ones) to add a component from KDE Frameworks. We don't rule this out and there's no prejudice against KDE; it just so happened that KDE Frameworks is one of most obvious reuse candidates but so far none of these components survived as libqmatrixclient deps. So we are cautious. * Never forget that libqmatrixclient is aimed to be a non-visual library; QtGui in dependencies is only driven by (entirely offscreen) dealing with QPixmaps. While there's a bunch of visual code (in C++ and QML) shared between libqmatrixclient-enabled _applications_, this is likely to end up in a separate (libqmatrixclient-enabled) library, rather than libqmatrixclient. ## Attribution @@ -1,4 +1,4 @@ -# Libqmatrixclient +# libQMatrixClient [![license](https://img.shields.io/github/license/QMatrixClient/libqmatrixclient.svg)](https://github.com/QMatrixClient/libqmatrixclient/blob/master/COPYING) ![status](https://img.shields.io/badge/status-beta-yellow.svg) @@ -6,35 +6,37 @@ [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1023/badge)](https://bestpractices.coreinfrastructure.org/projects/1023) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) -libqmatrixclient is a Qt5-based library to make IM clients for the [Matrix](https://matrix.org) protocol. It is the backbone of [Quaternion](https://github.com/QMatrixClient/Quaternion), [Tensor](https://matrix.org/docs/projects/client/tensor.html) and some other projects. +libQMatrixClient is a Qt5-based library to make IM clients for the [Matrix](https://matrix.org) protocol. It is the backbone of [Quaternion](https://github.com/QMatrixClient/Quaternion), [Tensor](https://matrix.org/docs/projects/client/tensor.html) and some other projects. ## Contacts -You can find authors of libqmatrixclient in the Matrix room: [#qmatrixclient:matrix.org](https://matrix.to/#/#qmatrixclient:matrix.org). +You can find authors of libQMatrixClient in the Matrix room: [#qmatrixclient:matrix.org](https://matrix.to/#/#qmatrixclient:matrix.org). You can also file issues at [the project's issue tracker](https://github.com/QMatrixClient/libqmatrixclient/issues). If you have what looks like a security issue, please see respective instructions in CONTRIBUTING.md. ## Building and usage -So far the library is typically used as a git submodule of another project (such as Quaternion); however it can be built separately (either as a static or as a dynamic library). There is no installation sequence outside of other projects so far (PRs are most welcome). +So far the library is typically used as a git submodule of another project (such as Quaternion); however it can be built separately (either as a static or as a dynamic library). As of version 0.2, the library can be installed and CMake package config files are provided; projects can use `find_package(QMatrixClient)` to setup their code with the installed library files. PRs to enable the same for qmake are most welcome. -The source code is hosted at GitHub: https://github.com/QMatrixClient/libqmatrixclient - checking out a certain commit or tag from GitHub (rather than downloading the archive) is the recommended way for one-off building. If you want to hack on the library as a part of another project (e.g. Quaternion), you're advised to make a recursive check out of that project and update the library submodule to its master branch. +The source code is hosted at GitHub: https://github.com/QMatrixClient/libqmatrixclient - checking out a certain commit or tag from GitHub (rather than downloading the archive) is the recommended way for one-off building. If you want to hack on the library as a part of another project (e.g. you are working on Quaternion but need to do some changes to the library code), you're advised to make a recursive check out of that project (in this case, Quaternion) and update the library submodule to its master branch. -There are very few tags so far; there will be more, as new versions are released. +Tags starting with `v` represent released versions; `rc` mark release candidates. ## Pre-requisites -- a Linux, OSX or Windows system (desktop versions tried; mobile Linux/Windows might work too) +- a Linux, OSX or Windows system (desktop versions tried; Ubuntu Touch is known to work; mobile Windows and iOS might work too but never tried) - For Ubuntu flavours - zesty or later (or a derivative) is good enough out of the box; older ones will need PPAs at least for a newer Qt; in particular, if you have xenial you're advised to add Kubuntu Backports PPA for it - a Git client to check out this repo -- CMake (from your package management system or [the official website](https://cmake.org/download/)) - Qt 5 (either Open Source or Commercial), version 5.6 or higher +- a build configuration tool: + - CMake (from your package management system or [the official website](https://cmake.org/download/)) + - or qmake (comes with Qt) - a C++ toolchain supported by your version of Qt (see a link for your platform at [the Qt's platform requirements page](http://doc.qt.io/qt-5/gettingstarted.html#platform-requirements)) - - GCC 5 (Windows, Linux, OSX), Clang 5 (Linux), Apple Clang 8.1 (OSX) and Visual C++ 2015 (Windows) are the oldest officially supported - - any build system that works with CMake should be fine: GNU Make, ninja (any platform), NMake, jom (Windows) are known to work. + - GCC 5 (Windows, Linux, OSX), Clang 5 (Linux), Apple Clang 8.1 (OSX) and Visual C++ 2015 (Windows) are the oldest officially supported; Clang 3.8 and GCC 4.9.2 are known to still work, maintenance patches for them are accepted + - any build system that works with CMake and/or qmake should be fine: GNU Make, ninja (any platform), NMake, jom (Windows) are known to work. #### Linux -Just install things from the list above using your preferred package manager. If your Qt package base is fine-grained you might want to take a look at `CMakeLists.txt` to figure out which specific libraries libqmatrixclient uses (or blindly run cmake and look at error messages). The library is entirely offscreen (Qt::Core and Qt::Network are essential) but it also depends on Qt::Gui in order to operate with avatar thumbnails. +Just install things from the list above using your preferred package manager. If your Qt package base is fine-grained you might want to run cmake/qmake and look at error messages. The library is entirely offscreen (QtCore and QtNetwork are essential) but it also depends on QtGui in order to handle avatar thumbnails. #### OS X -`brew install qt5` should get you Qt5. If you plan to use CMake, you may need to tell it about the path to Qt by passing `-DCMAKE_PREFIX_PATH=<where-Qt-installed>` +`brew install qt5` should get you a recent Qt5. If you plan to use CMake, you may need to tell it about the path to Qt by passing `-DCMAKE_PREFIX_PATH=<where-Qt-installed>` #### Windows 1. Install Qt5, using their official installer. @@ -51,15 +53,23 @@ cd build_dir cmake .. # Pass -DCMAKE_PREFIX_PATH and -DCMAKE_INSTALL_PREFIX here if needed cmake --build . --target all ``` -This will get you the compiled library in `build_dir` inside your project sources. Only static builds of libqmatrixclient are tested at the moment; experiments with dynamic builds are welcome. The two known projects to link with libqmatrixclient are Tensor and Quaternion; you should take a look at their source code before doing anything with libqmatrixclient on your own. +This will get you the compiled library in `build_dir` inside your project sources. Only static builds of libqmatrixclient are tested at the moment; experiments with dynamic builds are welcome. The three known projects to link with libqmatrixclient are Quaternion, uMatriks and Tensor; you should take a look at their source code before doing something big with libqmatrixclient on your own. For smaller things, taking a look at qmc-example should give you a basic idea of using the library. + +You can install the library with CMake: +``` +cmake --build . --target install +``` +This will also install cmake package config files; once this is done, you can use `examples/CMakeLists.txt` to compile the example with the _installed_ library. This file is a good starting point for your own CMake-based project using libQMatrixClient. ### qmake-based -The library only provides a .pri file with an intention to be included from a bigger project's .pro file. As a starting point you can use `qmc-example.pro` that will build a minimal example of library usage for you. In the root directory of the project sources: +The library provides a .pri file with an intention to be included from a bigger project's .pro file. As a starting point you can use `qmc-example.pro` that will build a minimal example of library usage for you. In the root directory of the project sources: ``` qmake qmc-example.pro make all ``` -This will get you `debug/qmc-example` and `release/qmc-example` console executables that login to the Matrix server at matrix.org with credentials of your choosing (pass the username and password as arguments) and run a sync long-polling loop, showing some information about received events. +This will get you `debug/qmc-example` and `release/qmc-example` console executables that login to the Matrix server at matrix.org with credentials of your choosing (pass the username and password as arguments), run a sync long-polling loop and do some tests of the library API. + +Installing the library with qmake is not possible; similarly, a .prl file is not provided. A PR to fix this is welcome. ## Troubleshooting diff --git a/cmake/QMatrixClientConfig.cmake b/cmake/QMatrixClientConfig.cmake new file mode 100644 index 00000000..900038a5 --- /dev/null +++ b/cmake/QMatrixClientConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/QMatrixClientTargets.cmake") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..49e0089a --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.1) + +# This CMakeLists file assumes that the library is installed to CMAKE_INSTALL_PREFIX +# and ignores the in-tree library code. You can use this to start work on your own client. + +project(qmc-example CXX) + +include(CheckCXXCompilerFlag) +if (NOT WIN32) + include(GNUInstallDirs) +endif(NOT WIN32) + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Debug' as none was specified") + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build" FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +if (NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR ".") +endif() + +if (NOT CMAKE_INSTALL_BINDIR) + set(CMAKE_INSTALL_BINDIR ".") +endif() + +if (NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "include") +endif() + +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 () + +find_package(Qt5 5.6 REQUIRED Network Gui) +get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) + +find_package(QMatrixClient REQUIRED) +get_filename_component(QMC_Prefix "${QMatrixClient_DIR}/../.." ABSOLUTE) + +message( STATUS "qmc-example configuration:" ) +if (CMAKE_BUILD_TYPE) + message( STATUS " Build type: ${CMAKE_BUILD_TYPE}") +endif(CMAKE_BUILD_TYPE) +message( STATUS " Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" ) +message( STATUS " Qt: ${Qt5_VERSION} at ${Qt5_Prefix}" ) +message( STATUS " QMatrixClient: ${QMatrixClient_VERSION} at ${QMC_Prefix}" ) + +set(example_SRCS qmc-example.cpp) + +add_executable(qmc-example ${example_SRCS}) +target_link_libraries(qmc-example Qt5::Core QMatrixClient) + +# Installation + +install (TARGETS qmc-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 23a1bff1..e2ec1d12 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -25,9 +25,11 @@ class QMCTest : public QObject void doTests(); void addAndRemoveTag(); void sendAndRedact(); - void checkRedactionOutcome(QString evtIdToRedact, RoomEventsRange events); + void checkRedactionOutcome(QString evtIdToRedact, + RoomEventsRange events); void markDirectChat(); - void checkDirectChatOutcome(); + void checkDirectChatOutcome( + const Connection::DirectChatsMap& added); void finalize(); private: @@ -211,11 +213,12 @@ void QMCTest::checkRedactionOutcome(QString evtIdToRedact, void QMCTest::markDirectChat() { - if (c->isDirectChat(targetRoom->id())) + if (targetRoom->directChatUsers().contains(c->user())) { cout << "Warning: the room is already a direct chat," " only unmarking will be tested" << endl; - checkDirectChatOutcome(); + checkDirectChatOutcome({{ c->user(), targetRoom->id() }}); + return; } // Connect first because the signal is emitted synchronously. connect(c.data(), &Connection::directChatsListChanged, @@ -224,11 +227,18 @@ void QMCTest::markDirectChat() c->addToDirectChats(targetRoom, c->user()); } -void QMCTest::checkDirectChatOutcome() +void QMCTest::checkDirectChatOutcome(const Connection::DirectChatsMap& added) { disconnect(c.data(), &Connection::directChatsListChanged, nullptr, nullptr); if (!c->isDirectChat(targetRoom->id())) { + cout << "The room has not been marked as a direct chat" << endl; + QMC_CHECK("Direct chat test", false); + return; + } + if (!added.contains(c->user(), targetRoom->id())) + { + cout << "The room has not been listed in new direct chats" << endl; QMC_CHECK("Direct chat test", false); return; } diff --git a/avatar.cpp b/lib/avatar.cpp index 1ff2aae1..1ff2aae1 100644 --- a/avatar.cpp +++ b/lib/avatar.cpp diff --git a/connection.cpp b/lib/connection.cpp index 2d7235b9..600ab396 100644 --- a/connection.cpp +++ b/lib/connection.cpp @@ -47,7 +47,22 @@ using namespace QMatrixClient; -using DirectChatsMap = QMultiHash<const User*, QString>; +// This is very much Qt-specific; STL iterators don't have key() and value() +template <typename HashT, typename Pred> +HashT erase_if(HashT& hashMap, Pred pred) +{ + HashT removals; + for (auto it = hashMap.begin(); it != hashMap.end();) + { + if (pred(it)) + { + removals.insert(it.key(), it.value()); + it = hashMap.erase(it); + } else + ++it; + } + return removals; +} class Connection::Private { @@ -80,7 +95,8 @@ class Connection::Private void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); - void broadcastDirectChatUpdates(); + void broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals); }; Connection::Connection(const QUrl& server, QObject* parent) @@ -132,7 +148,7 @@ void Connection::resolveServer(const QString& mxidOrDomain) auto domain = maybeBaseUrl.host(); qCDebug(MAIN) << "Finding the server" << domain; // Check if the Matrix server has a dedicated service record. - QDnsLookup* dns = new QDnsLookup(); + auto* dns = new QDnsLookup(); dns->setType(QDnsLookup::SRV); dns->setName("_matrix._tcp." + domain); @@ -248,13 +264,13 @@ void Connection::sync(int timeout) const QString filter { R"({"room": { "timeline": { "limit": 100 } } })" }; auto job = d->syncJob = callApi<SyncJob>(d->data->lastEvent(), filter, timeout); - connect( job, &SyncJob::success, [this, job] { + connect( job, &SyncJob::success, this, [this, job] { onSyncSuccess(job->takeData()); d->syncJob = nullptr; emit syncDone(); }); connect( job, &SyncJob::retryScheduled, this, &Connection::networkError); - connect( job, &SyncJob::failure, [this, job] { + connect( job, &SyncJob::failure, this, [this, job] { d->syncJob = nullptr; if (job->error() == BaseJob::ContentAccessError) emit loginError(job->errorString()); @@ -290,24 +306,38 @@ void Connection::onSyncSuccess(SyncData &&data) { { if (accountEvent->type() == EventType::DirectChat) { - DirectChatsMap newDirectChats; - const auto* event = static_cast<DirectChatEvent*>(accountEvent.get()); - auto usersToDCs = event->usersToDirectChats(); + const auto usersToDCs = + unique_ptr_cast<DirectChatEvent>(accountEvent) + ->usersToDirectChats(); + DirectChatsMap removals = + erase_if(d->directChats, [&usersToDCs] (auto it) { + return !usersToDCs.contains(it.key()->id(), it.value()); + }); + if (MAIN().isDebugEnabled()) + for (auto it = removals.begin(); it != removals.end(); ++it) + qCDebug(MAIN) << it.value() + << "is no more a direct chat with" << it.key()->id(); + + DirectChatsMap additions; for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) { - newDirectChats.insert(user(it.key()), it.value()); - qCDebug(MAIN) << "Marked room" << it.value() - << "as a direct chat with" << it.key(); - } - if (newDirectChats != d->directChats) - { - d->directChats = newDirectChats; - emit directChatsListChanged(); + const auto* u = user(it.key()); + if (!d->directChats.contains(u, it.value())) + { + additions.insert(u, it.value()); + d->directChats.insert(u, it.value()); + qCDebug(MAIN) << "Marked room" << it.value() + << "as a direct chat with" << u->id(); + } } + if (!additions.isEmpty() || !removals.isEmpty()) + emit directChatsListChanged(additions, removals); + continue; } d->accountData[accountEvent->jsonType()] = accountEvent->contentJson().toVariantHash(); + emit accountDataChanged(accountEvent->jsonType()); } } @@ -422,7 +452,7 @@ CreateRoomJob* Connection::createRoom(RoomVisibility visibility, bool isDirect, bool guestsCanJoin, const QVector<CreateRoomJob::StateEvent>& initialState, const QVector<CreateRoomJob::Invite3pid>& invite3pids, - const QJsonObject creationContent) + const QJsonObject& creationContent) { auto job = callApi<CreateRoomJob>( visibility == PublishRoom ? "public" : "private", alias, name, @@ -444,8 +474,11 @@ void Connection::doInDirectChat(const QString& userId, { // There can be more than one DC; find the first valid, and delete invalid // (left/forgotten) ones along the way. - for (auto roomId: d->directChats.values(user(userId))) + const auto* u = user(userId); + for (auto it = d->directChats.find(u); + it != d->directChats.end() && it.key() == u; ++it) { + const auto& roomId = *it; if (auto r = room(roomId, JoinState::Join)) { Q_ASSERT(r->id() == roomId); @@ -619,10 +652,20 @@ QHash< QPair<QString, bool>, Room* > Connection::roomMap() const return roomMap; } +bool Connection::hasAccountData(const QString& type) const +{ + return d->accountData.contains(type); +} + +QVariantHash Connection::accountData(const QString& type) const +{ + return d->accountData.value(type); +} + QHash<QString, QVector<Room*>> Connection::tagsToRooms() const { QHash<QString, QVector<Room*>> result; - for (auto* r: d->roomMap) + for (auto* r: qAsConst(d->roomMap)) { for (const auto& tagName: r->tagNames()) result[tagName].push_back(r); @@ -638,7 +681,7 @@ QHash<QString, QVector<Room*>> Connection::tagsToRooms() const QStringList Connection::tagNames() const { QStringList tags ({FavouriteTag}); - for (auto* r: d->roomMap) + for (auto* r: qAsConst(d->roomMap)) for (const auto& tag: r->tagNames()) if (tag != LowPriorityTag && !tags.contains(tag)) tags.push_back(tag); @@ -654,19 +697,31 @@ QVector<Room*> Connection::roomsWithTag(const QString& tagName) const return rooms; } -QJsonObject toJson(const DirectChatsMap& directChats) +Connection::DirectChatsMap Connection::directChats() const +{ + return d->directChats; +} + +QJsonObject toJson(const Connection::DirectChatsMap& directChats) { QJsonObject json; - for (auto it = directChats.keyBegin(); it != directChats.keyEnd(); ++it) - json.insert((*it)->id(), toJson(directChats.values(*it))); + for (auto it = directChats.begin(); it != directChats.end();) + { + QJsonArray roomIds; + const auto* user = it.key(); + for (; it != directChats.end() && it.key() == user; ++it) + roomIds.append(*it); + json.insert(user->id(), roomIds); + } return json; } -void Connection::Private::broadcastDirectChatUpdates() +void Connection::Private::broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals) { q->callApi<SetAccountDataJob>(userId, QStringLiteral("m.direct"), toJson(directChats)); - emit q->directChatsListChanged(); + emit q->directChatsListChanged(additions, removals); } void Connection::addToDirectChats(const Room* room, const User* user) @@ -675,7 +730,8 @@ void Connection::addToDirectChats(const Room* room, const User* user) if (d->directChats.contains(user, room->id())) return; d->directChats.insert(user, room->id()); - d->broadcastDirectChatUpdates(); + DirectChatsMap additions { { user, room->id() } }; + d->broadcastDirectChatUpdates(additions, {}); } void Connection::removeFromDirectChats(const QString& roomId, const User* user) @@ -684,17 +740,17 @@ void Connection::removeFromDirectChats(const QString& roomId, const User* user) if ((user != nullptr && !d->directChats.contains(user, roomId)) || d->directChats.key(roomId) == nullptr) return; + + DirectChatsMap removals; if (user != nullptr) + { + removals.insert(user, roomId); d->directChats.remove(user, roomId); + } else - for (auto it = d->directChats.begin(); it != d->directChats.end();) - { - if (it.value() == roomId) - it = d->directChats.erase(it); - else - ++it; - } - d->broadcastDirectChatUpdates(); + removals = erase_if(d->directChats, + [&roomId] (auto it) { return it.value() == roomId; }); + d->broadcastDirectChatUpdates({}, removals); } bool Connection::isDirectChat(const QString& roomId) const diff --git a/connection.h b/lib/connection.h index c6d543ec..be414931 100644 --- a/connection.h +++ b/lib/connection.h @@ -64,6 +64,8 @@ namespace QMatrixClient using user_factory_t = std::function<User*(Connection*, const QString&)>; + using DirectChatsMap = QMultiHash<const User*, QString>; + enum RoomVisibility { PublishRoom, UnpublishRoom }; // FIXME: Should go inside CreateRoomJob explicit Connection(QObject* parent = nullptr); @@ -76,6 +78,18 @@ namespace QMatrixClient */ QHash<QPair<QString, bool>, Room*> roomMap() const; + /** Check whether the account has data of the given type + * Direct chats map is not supported by this method _yet_. + */ + bool hasAccountData(const QString& type) const; + + /** Get a generic account data event of the given type + * This returns a generic hashmap for any account data event + * stored on the server. Direct chats map cannot be retrieved + * using this method _yet_; use directChats() instead. + */ + QVariantHash accountData(const QString& type) const; + /** Get all Invited and Joined rooms grouped by tag * \return a hashmap from tag name to a vector of room pointers, * sorted by their order in the tag - details are at @@ -114,16 +128,18 @@ namespace QMatrixClient /** Check whether the room id corresponds to a direct chat */ bool isDirectChat(const QString& roomId) const; + /** Get the whole map from users to direct chat rooms */ + DirectChatsMap directChats() const; + /** Retrieve the list of users the room is a direct chat with * @return The list of users for which this room is marked as * a direct chat; an empty list if the room is not a direct chat */ QList<const User*> directChatUsers(const Room* room) const; + /** Get the full list of users known to this account */ QMap<QString, User*> users() const; - // FIXME: Convert Q_INVOKABLEs to Q_PROPERTIES - // (breaks back-compatibility) QUrl homeserver() const; Q_INVOKABLE Room* room(const QString& roomId, JoinStates states = JoinState::Invite|JoinState::Join) const; @@ -256,7 +272,7 @@ namespace QMatrixClient bool guestsCanJoin = false, const QVector<CreateRoomJob::StateEvent>& initialState = {}, const QVector<CreateRoomJob::Invite3pid>& invite3pids = {}, - const QJsonObject creationContent = {}); + const QJsonObject& creationContent = {}); /** Get a direct chat with a single user * This method may return synchronously or asynchoronously depending @@ -410,6 +426,9 @@ namespace QMatrixClient */ void createdRoom(Room* room); + /** Account data (except direct chats) have changed */ + void accountDataChanged(QString type); + /** The direct chat room is ready for using * This signal is emitted upon any successful outcome from * requestDirectChat. @@ -421,7 +440,8 @@ namespace QMatrixClient * to direct chat rooms is changed (because of either local updates * or a different list arrived from the server). */ - void directChatsListChanged(); + void directChatsListChanged(DirectChatsMap additions, + DirectChatsMap removals); void cacheStateChanged(); diff --git a/connectiondata.cpp b/lib/connectiondata.cpp index 4e9bc77e..4e9bc77e 100644 --- a/connectiondata.cpp +++ b/lib/connectiondata.cpp diff --git a/connectiondata.h b/lib/connectiondata.h index 7a2f2e90..7a2f2e90 100644 --- a/connectiondata.h +++ b/lib/connectiondata.h diff --git a/converters.h b/lib/converters.h index bba298e0..bba298e0 100644 --- a/converters.h +++ b/lib/converters.h diff --git a/events/accountdataevents.h b/lib/events/accountdataevents.h index f3ba27bb..f3ba27bb 100644 --- a/events/accountdataevents.h +++ b/lib/events/accountdataevents.h diff --git a/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 7049d967..63d638a3 100644 --- a/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -29,8 +29,15 @@ DirectChatEvent::DirectChatEvent(const QJsonObject& obj) QMultiHash<QString, QString> DirectChatEvent::usersToDirectChats() const { QMultiHash<QString, QString> result; - for (auto it = contentJson().begin(); it != contentJson().end(); ++it) - for (auto roomIdValue: it.value().toArray()) + const auto json = contentJson(); + for (auto it = json.begin(); it != json.end(); ++it) + { + // Beware of range-for's over temporary returned from temporary + // (see the bottom of + // http://en.cppreference.com/w/cpp/language/range-for#Explanation) + const auto roomIds = it.value().toArray(); + for (const auto& roomIdValue: roomIds) result.insert(it.key(), roomIdValue.toString()); + } return result; } diff --git a/events/directchatevent.h b/lib/events/directchatevent.h index 2b0ad0a0..2b0ad0a0 100644 --- a/events/directchatevent.h +++ b/lib/events/directchatevent.h diff --git a/events/event.cpp b/lib/events/event.cpp index 8ddf3945..8ddf3945 100644 --- a/events/event.cpp +++ b/lib/events/event.cpp diff --git a/events/event.h b/lib/events/event.h index eccfec41..d614115a 100644 --- a/events/event.h +++ b/lib/events/event.h @@ -141,7 +141,7 @@ namespace QMatrixClient // The below line accommodates the difference in size types of // STL and Qt containers. this->reserve(static_cast<size_type>(objs.size())); - for (auto objValue: objs) + for (const auto& objValue: objs) this->emplace_back(makeEvent<EventT>(objValue.toObject())); } }; diff --git a/events/eventcontent.cpp b/lib/events/eventcontent.cpp index f5974b46..f5974b46 100644 --- a/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp diff --git a/events/eventcontent.h b/lib/events/eventcontent.h index 9d44aec0..9d44aec0 100644 --- a/events/eventcontent.h +++ b/lib/events/eventcontent.h diff --git a/events/receiptevent.cpp b/lib/events/receiptevent.cpp index 7555db82..7555db82 100644 --- a/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp diff --git a/events/receiptevent.h b/lib/events/receiptevent.h index 5b99ae3f..5b99ae3f 100644 --- a/events/receiptevent.h +++ b/lib/events/receiptevent.h diff --git a/events/redactionevent.cpp b/lib/events/redactionevent.cpp index bf467718..bf467718 100644 --- a/events/redactionevent.cpp +++ b/lib/events/redactionevent.cpp diff --git a/events/redactionevent.h b/lib/events/redactionevent.h index fa6902ab..fa6902ab 100644 --- a/events/redactionevent.h +++ b/lib/events/redactionevent.h diff --git a/events/roomavatarevent.cpp b/lib/events/roomavatarevent.cpp index 7a5f82a1..7a5f82a1 100644 --- a/events/roomavatarevent.cpp +++ b/lib/events/roomavatarevent.cpp diff --git a/events/roomavatarevent.h b/lib/events/roomavatarevent.h index ccfe8fbf..ccfe8fbf 100644 --- a/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h diff --git a/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index 76b003c2..76b003c2 100644 --- a/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp diff --git a/events/roommemberevent.h b/lib/events/roommemberevent.h index 89b970c9..89b970c9 100644 --- a/events/roommemberevent.h +++ b/lib/events/roommemberevent.h diff --git a/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index dec0ca50..1a4e74bf 100644 --- a/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -87,7 +87,7 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) _plainBody = content["body"].toString(); _msgtype = content["msgtype"].toString(); - for (auto mt: msgTypes) + for (const auto& mt: msgTypes) if (mt.jsonType == _msgtype) _content.reset(mt.maker(content)); diff --git a/events/roommessageevent.h b/lib/events/roommessageevent.h index a55564ed..a55564ed 100644 --- a/events/roommessageevent.h +++ b/lib/events/roommessageevent.h diff --git a/events/simplestateevents.h b/lib/events/simplestateevents.h index 6b0cd51a..6b0cd51a 100644 --- a/events/simplestateevents.h +++ b/lib/events/simplestateevents.h diff --git a/events/typingevent.cpp b/lib/events/typingevent.cpp index a4d3bae4..a4d3bae4 100644 --- a/events/typingevent.cpp +++ b/lib/events/typingevent.cpp diff --git a/events/typingevent.h b/lib/events/typingevent.h index 8c9551a4..8c9551a4 100644 --- a/events/typingevent.h +++ b/lib/events/typingevent.h diff --git a/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 0303af39..696861fb 100644 --- a/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -241,6 +241,8 @@ void BaseJob::sendRequest() connect( d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply ); if (d->reply->isRunning()) { + connect( d->reply.data(), &QNetworkReply::metaDataChanged, + this, &BaseJob::checkReply); connect( d->reply.data(), &QNetworkReply::uploadProgress, this, &BaseJob::uploadProgress); connect( d->reply.data(), &QNetworkReply::downloadProgress, @@ -253,13 +255,22 @@ void BaseJob::sendRequest() qCWarning(d->logCat) << this << "request could not start"; } +void BaseJob::checkReply() +{ + setStatus(doCheckReply(d->reply.data())); +} + void BaseJob::gotReply() { - setStatus(checkReply(d->reply.data())); + checkReply(); + qCDebug(d->logCat).nospace().noquote() << this << " returned HTTP code " + << d->reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() + << ": " << (d->reply->error() == QNetworkReply::NoError ? + "Success" : d->reply->errorString()) + << " (URL: " << d->reply->url().toDisplayString() << ")"; if (status().good()) setStatus(parseReply(d->reply.data())); - else - { + else { const auto body = d->reply->readAll(); if (!body.isEmpty()) { @@ -284,7 +295,7 @@ void BaseJob::gotReply() // Shortcut to retry instead of executing finishJob() stop(); qCWarning(d->logCat) - << this << "will retry in" << retryInterval; + << this << "will retry in" << retryInterval << "ms"; d->retryTimer.start(retryInterval); emit retryScheduled(d->retriesTaken, retryInterval); return; @@ -323,15 +334,10 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) return false; } -BaseJob::Status BaseJob::checkReply(QNetworkReply* reply) const +BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const { const auto httpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - qCDebug(d->logCat).nospace().noquote() << this << " returned HTTP code " - << httpCode << ": " - << (reply->error() == QNetworkReply::NoError ? - "Success" : reply->errorString()) - << " (URL: " << reply->url().toDisplayString() << ")"; if (httpCode == 429) // Qt doesn't know about it yet return { TooManyRequestsError, tr("Too many requests") }; diff --git a/jobs/basejob.h b/lib/jobs/basejob.h index ed630a67..f243066f 100644 --- a/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -101,9 +101,9 @@ namespace QMatrixClient bool good() const { return code < ErrorLevel; } friend QDebug operator<<(QDebug dbg, const Status& s) { - QDebug(dbg).noquote().nospace() + QDebugStateSaver _s(dbg); + return dbg.noquote().nospace() << s.code << ": " << s.message; - return dbg; } int code; @@ -248,7 +248,7 @@ namespace QMatrixClient * * @see gotReply */ - virtual Status checkReply(QNetworkReply* reply) const; + virtual Status doCheckReply(QNetworkReply* reply) const; /** * Processes the reply. By default, parses the reply into @@ -286,6 +286,7 @@ namespace QMatrixClient private slots: void sendRequest(); + void checkReply(); void gotReply(); private: diff --git a/jobs/checkauthmethods.cpp b/lib/jobs/checkauthmethods.cpp index 117def89..117def89 100644 --- a/jobs/checkauthmethods.cpp +++ b/lib/jobs/checkauthmethods.cpp diff --git a/jobs/checkauthmethods.h b/lib/jobs/checkauthmethods.h index 647f3db6..647f3db6 100644 --- a/jobs/checkauthmethods.h +++ b/lib/jobs/checkauthmethods.h diff --git a/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 6a3d8483..2bf9dd8f 100644 --- a/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -62,6 +62,8 @@ void DownloadFileJob::beforeStart(const ConnectionData*) void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply) { connect(reply, &QNetworkReply::metaDataChanged, this, [this,reply] { + if (!status().good()) + return; auto sizeHeader = reply->header(QNetworkRequest::ContentLengthHeader); if (sizeHeader.isValid()) { @@ -77,15 +79,15 @@ void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply) } }); connect(reply, &QIODevice::readyRead, this, [this,reply] { - auto bytes = reply->read(reply->bytesAvailable()); - if (bytes.isEmpty()) - { - qCWarning(JOBS) - << "Unexpected empty chunk when downloading from" - << reply->url() << "to" << d->tempFile->fileName(); - } else { - d->tempFile->write(bytes); - } + if (!status().good()) + return; + auto bytes = reply->read(reply->bytesAvailable()); + if (!bytes.isEmpty()) + d->tempFile->write(bytes); + else + qCWarning(JOBS) + << "Unexpected empty chunk when downloading from" + << reply->url() << "to" << d->tempFile->fileName(); }); } diff --git a/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index 1815a7c8..1815a7c8 100644 --- a/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h diff --git a/jobs/generated/account-data.cpp b/lib/jobs/generated/account-data.cpp index 35ee94c0..35ee94c0 100644 --- a/jobs/generated/account-data.cpp +++ b/lib/jobs/generated/account-data.cpp diff --git a/jobs/generated/account-data.h b/lib/jobs/generated/account-data.h index 69ad9fb4..69ad9fb4 100644 --- a/jobs/generated/account-data.h +++ b/lib/jobs/generated/account-data.h diff --git a/jobs/generated/administrative_contact.cpp b/lib/jobs/generated/administrative_contact.cpp index 1af57941..1af57941 100644 --- a/jobs/generated/administrative_contact.cpp +++ b/lib/jobs/generated/administrative_contact.cpp diff --git a/jobs/generated/administrative_contact.h b/lib/jobs/generated/administrative_contact.h index c8429d39..c8429d39 100644 --- a/jobs/generated/administrative_contact.h +++ b/lib/jobs/generated/administrative_contact.h diff --git a/jobs/generated/banning.cpp b/lib/jobs/generated/banning.cpp index f66b27b6..f66b27b6 100644 --- a/jobs/generated/banning.cpp +++ b/lib/jobs/generated/banning.cpp diff --git a/jobs/generated/banning.h b/lib/jobs/generated/banning.h index 2d6fbd9b..2d6fbd9b 100644 --- a/jobs/generated/banning.h +++ b/lib/jobs/generated/banning.h diff --git a/jobs/generated/content-repo.cpp b/lib/jobs/generated/content-repo.cpp index 51011251..51011251 100644 --- a/jobs/generated/content-repo.cpp +++ b/lib/jobs/generated/content-repo.cpp diff --git a/jobs/generated/content-repo.h b/lib/jobs/generated/content-repo.h index b4ea562f..b4ea562f 100644 --- a/jobs/generated/content-repo.h +++ b/lib/jobs/generated/content-repo.h diff --git a/jobs/generated/create_room.cpp b/lib/jobs/generated/create_room.cpp index de7807b5..de7807b5 100644 --- a/jobs/generated/create_room.cpp +++ b/lib/jobs/generated/create_room.cpp diff --git a/jobs/generated/create_room.h b/lib/jobs/generated/create_room.h index b479615a..b479615a 100644 --- a/jobs/generated/create_room.h +++ b/lib/jobs/generated/create_room.h diff --git a/jobs/generated/directory.cpp b/lib/jobs/generated/directory.cpp index 9428dcee..9428dcee 100644 --- a/jobs/generated/directory.cpp +++ b/lib/jobs/generated/directory.cpp diff --git a/jobs/generated/directory.h b/lib/jobs/generated/directory.h index 87591240..87591240 100644 --- a/jobs/generated/directory.h +++ b/lib/jobs/generated/directory.h diff --git a/jobs/generated/inviting.cpp b/lib/jobs/generated/inviting.cpp index d2ee2107..d2ee2107 100644 --- a/jobs/generated/inviting.cpp +++ b/lib/jobs/generated/inviting.cpp diff --git a/jobs/generated/inviting.h b/lib/jobs/generated/inviting.h index eaa884df..eaa884df 100644 --- a/jobs/generated/inviting.h +++ b/lib/jobs/generated/inviting.h diff --git a/jobs/generated/kicking.cpp b/lib/jobs/generated/kicking.cpp index bf2490b7..bf2490b7 100644 --- a/jobs/generated/kicking.cpp +++ b/lib/jobs/generated/kicking.cpp diff --git a/jobs/generated/kicking.h b/lib/jobs/generated/kicking.h index 3814bef7..3814bef7 100644 --- a/jobs/generated/kicking.h +++ b/lib/jobs/generated/kicking.h diff --git a/jobs/generated/leaving.cpp b/lib/jobs/generated/leaving.cpp index fbc40d11..fbc40d11 100644 --- a/jobs/generated/leaving.cpp +++ b/lib/jobs/generated/leaving.cpp diff --git a/jobs/generated/leaving.h b/lib/jobs/generated/leaving.h index 9bae2363..9bae2363 100644 --- a/jobs/generated/leaving.h +++ b/lib/jobs/generated/leaving.h diff --git a/jobs/generated/list_joined_rooms.cpp b/lib/jobs/generated/list_joined_rooms.cpp index f902f94c..f902f94c 100644 --- a/jobs/generated/list_joined_rooms.cpp +++ b/lib/jobs/generated/list_joined_rooms.cpp diff --git a/jobs/generated/list_joined_rooms.h b/lib/jobs/generated/list_joined_rooms.h index 768f5166..768f5166 100644 --- a/jobs/generated/list_joined_rooms.h +++ b/lib/jobs/generated/list_joined_rooms.h diff --git a/jobs/generated/list_public_rooms.cpp b/lib/jobs/generated/list_public_rooms.cpp index 39653300..39653300 100644 --- a/jobs/generated/list_public_rooms.cpp +++ b/lib/jobs/generated/list_public_rooms.cpp diff --git a/jobs/generated/list_public_rooms.h b/lib/jobs/generated/list_public_rooms.h index 5c281de3..5c281de3 100644 --- a/jobs/generated/list_public_rooms.h +++ b/lib/jobs/generated/list_public_rooms.h diff --git a/jobs/generated/login.cpp b/lib/jobs/generated/login.cpp index a4dab428..a4dab428 100644 --- a/jobs/generated/login.cpp +++ b/lib/jobs/generated/login.cpp diff --git a/jobs/generated/login.h b/lib/jobs/generated/login.h index 3ac955d4..3ac955d4 100644 --- a/jobs/generated/login.h +++ b/lib/jobs/generated/login.h diff --git a/jobs/generated/logout.cpp b/lib/jobs/generated/logout.cpp index 83139842..83139842 100644 --- a/jobs/generated/logout.cpp +++ b/lib/jobs/generated/logout.cpp diff --git a/jobs/generated/logout.h b/lib/jobs/generated/logout.h index 7640ba55..7640ba55 100644 --- a/jobs/generated/logout.h +++ b/lib/jobs/generated/logout.h diff --git a/jobs/generated/profile.cpp b/lib/jobs/generated/profile.cpp index 1f7092d7..1f7092d7 100644 --- a/jobs/generated/profile.cpp +++ b/lib/jobs/generated/profile.cpp diff --git a/jobs/generated/profile.h b/lib/jobs/generated/profile.h index 024130f5..024130f5 100644 --- a/jobs/generated/profile.h +++ b/lib/jobs/generated/profile.h diff --git a/jobs/generated/pusher.cpp b/lib/jobs/generated/pusher.cpp index 4fddac45..4fddac45 100644 --- a/jobs/generated/pusher.cpp +++ b/lib/jobs/generated/pusher.cpp diff --git a/jobs/generated/pusher.h b/lib/jobs/generated/pusher.h index 36576996..36576996 100644 --- a/jobs/generated/pusher.h +++ b/lib/jobs/generated/pusher.h diff --git a/jobs/generated/receipts.cpp b/lib/jobs/generated/receipts.cpp index 83c38b6f..83c38b6f 100644 --- a/jobs/generated/receipts.cpp +++ b/lib/jobs/generated/receipts.cpp diff --git a/jobs/generated/receipts.h b/lib/jobs/generated/receipts.h index 9eb7a489..9eb7a489 100644 --- a/jobs/generated/receipts.h +++ b/lib/jobs/generated/receipts.h diff --git a/jobs/generated/redaction.cpp b/lib/jobs/generated/redaction.cpp index 0da35dfc..0da35dfc 100644 --- a/jobs/generated/redaction.cpp +++ b/lib/jobs/generated/redaction.cpp diff --git a/jobs/generated/redaction.h b/lib/jobs/generated/redaction.h index e3b3ff4f..e3b3ff4f 100644 --- a/jobs/generated/redaction.h +++ b/lib/jobs/generated/redaction.h diff --git a/jobs/generated/room_send.cpp b/lib/jobs/generated/room_send.cpp index c9a3280d..c9a3280d 100644 --- a/jobs/generated/room_send.cpp +++ b/lib/jobs/generated/room_send.cpp diff --git a/jobs/generated/room_send.h b/lib/jobs/generated/room_send.h index d20ce523..d20ce523 100644 --- a/jobs/generated/room_send.h +++ b/lib/jobs/generated/room_send.h diff --git a/jobs/generated/tags.cpp b/lib/jobs/generated/tags.cpp index dc4faf04..dc4faf04 100644 --- a/jobs/generated/tags.cpp +++ b/lib/jobs/generated/tags.cpp diff --git a/jobs/generated/tags.h b/lib/jobs/generated/tags.h index 7a375527..7a375527 100644 --- a/jobs/generated/tags.h +++ b/lib/jobs/generated/tags.h diff --git a/jobs/generated/third_party_membership.cpp b/lib/jobs/generated/third_party_membership.cpp index b637d481..b637d481 100644 --- a/jobs/generated/third_party_membership.cpp +++ b/lib/jobs/generated/third_party_membership.cpp diff --git a/jobs/generated/third_party_membership.h b/lib/jobs/generated/third_party_membership.h index c7b5214e..c7b5214e 100644 --- a/jobs/generated/third_party_membership.h +++ b/lib/jobs/generated/third_party_membership.h diff --git a/jobs/generated/to_device.cpp b/lib/jobs/generated/to_device.cpp index cfb860c7..cfb860c7 100644 --- a/jobs/generated/to_device.cpp +++ b/lib/jobs/generated/to_device.cpp diff --git a/jobs/generated/to_device.h b/lib/jobs/generated/to_device.h index 0de8fb0a..0de8fb0a 100644 --- a/jobs/generated/to_device.h +++ b/lib/jobs/generated/to_device.h diff --git a/jobs/generated/typing.cpp b/lib/jobs/generated/typing.cpp index fa700290..fa700290 100644 --- a/jobs/generated/typing.cpp +++ b/lib/jobs/generated/typing.cpp diff --git a/jobs/generated/typing.h b/lib/jobs/generated/typing.h index 0495ed0a..0495ed0a 100644 --- a/jobs/generated/typing.h +++ b/lib/jobs/generated/typing.h diff --git a/jobs/generated/users.cpp b/lib/jobs/generated/users.cpp index 33da4b43..33da4b43 100644 --- a/jobs/generated/users.cpp +++ b/lib/jobs/generated/users.cpp diff --git a/jobs/generated/users.h b/lib/jobs/generated/users.h index 7be250a5..7be250a5 100644 --- a/jobs/generated/users.h +++ b/lib/jobs/generated/users.h diff --git a/jobs/generated/versions.cpp b/lib/jobs/generated/versions.cpp index b12594ca..b12594ca 100644 --- a/jobs/generated/versions.cpp +++ b/lib/jobs/generated/versions.cpp diff --git a/jobs/generated/versions.h b/lib/jobs/generated/versions.h index 18f6bb44..18f6bb44 100644 --- a/jobs/generated/versions.h +++ b/lib/jobs/generated/versions.h diff --git a/jobs/generated/whoami.cpp b/lib/jobs/generated/whoami.cpp index cc38fa4d..cc38fa4d 100644 --- a/jobs/generated/whoami.cpp +++ b/lib/jobs/generated/whoami.cpp diff --git a/jobs/generated/whoami.h b/lib/jobs/generated/whoami.h index 835232ee..835232ee 100644 --- a/jobs/generated/whoami.h +++ b/lib/jobs/generated/whoami.h diff --git a/jobs/gtad.yaml b/lib/jobs/gtad.yaml index 78c879e4..78c879e4 100644 --- a/jobs/gtad.yaml +++ b/lib/jobs/gtad.yaml diff --git a/jobs/joinroomjob.cpp b/lib/jobs/joinroomjob.cpp index 66a75089..66a75089 100644 --- a/jobs/joinroomjob.cpp +++ b/lib/jobs/joinroomjob.cpp diff --git a/jobs/joinroomjob.h b/lib/jobs/joinroomjob.h index f3ba216f..f3ba216f 100644 --- a/jobs/joinroomjob.h +++ b/lib/jobs/joinroomjob.h diff --git a/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index dda1cdb4..dda1cdb4 100644 --- a/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp diff --git a/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h index 6e0b94f3..6e0b94f3 100644 --- a/jobs/mediathumbnailjob.h +++ b/lib/jobs/mediathumbnailjob.h diff --git a/jobs/passwordlogin.cpp b/lib/jobs/passwordlogin.cpp index 8abfe66a..8abfe66a 100644 --- a/jobs/passwordlogin.cpp +++ b/lib/jobs/passwordlogin.cpp diff --git a/jobs/passwordlogin.h b/lib/jobs/passwordlogin.h index fb8777a3..fb8777a3 100644 --- a/jobs/passwordlogin.h +++ b/lib/jobs/passwordlogin.h diff --git a/jobs/postreadmarkersjob.h b/lib/jobs/postreadmarkersjob.h index d0198821..d0198821 100644 --- a/jobs/postreadmarkersjob.h +++ b/lib/jobs/postreadmarkersjob.h diff --git a/jobs/postreceiptjob.cpp b/lib/jobs/postreceiptjob.cpp index 4572d74c..4572d74c 100644 --- a/jobs/postreceiptjob.cpp +++ b/lib/jobs/postreceiptjob.cpp diff --git a/jobs/postreceiptjob.h b/lib/jobs/postreceiptjob.h index 23df7c05..23df7c05 100644 --- a/jobs/postreceiptjob.h +++ b/lib/jobs/postreceiptjob.h diff --git a/jobs/preamble.mustache b/lib/jobs/preamble.mustache index 3ba87d61..3ba87d61 100644 --- a/jobs/preamble.mustache +++ b/lib/jobs/preamble.mustache diff --git a/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp index 5cb62221..5cb62221 100644 --- a/jobs/requestdata.cpp +++ b/lib/jobs/requestdata.cpp diff --git a/jobs/requestdata.h b/lib/jobs/requestdata.h index aa03b744..aa03b744 100644 --- a/jobs/requestdata.h +++ b/lib/jobs/requestdata.h diff --git a/jobs/roommessagesjob.cpp b/lib/jobs/roommessagesjob.cpp index e5568f17..e5568f17 100644 --- a/jobs/roommessagesjob.cpp +++ b/lib/jobs/roommessagesjob.cpp diff --git a/jobs/roommessagesjob.h b/lib/jobs/roommessagesjob.h index 7b3fd9c9..7b3fd9c9 100644 --- a/jobs/roommessagesjob.h +++ b/lib/jobs/roommessagesjob.h diff --git a/jobs/sendeventjob.cpp b/lib/jobs/sendeventjob.cpp index f5190d4b..f5190d4b 100644 --- a/jobs/sendeventjob.cpp +++ b/lib/jobs/sendeventjob.cpp diff --git a/jobs/sendeventjob.h b/lib/jobs/sendeventjob.h index 3a11eb6a..3a11eb6a 100644 --- a/jobs/sendeventjob.h +++ b/lib/jobs/sendeventjob.h diff --git a/jobs/setroomstatejob.cpp b/lib/jobs/setroomstatejob.cpp index c2beb87b..c2beb87b 100644 --- a/jobs/setroomstatejob.cpp +++ b/lib/jobs/setroomstatejob.cpp diff --git a/jobs/setroomstatejob.h b/lib/jobs/setroomstatejob.h index b7e6d4a1..b7e6d4a1 100644 --- a/jobs/setroomstatejob.h +++ b/lib/jobs/setroomstatejob.h diff --git a/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index 435dfd0e..435dfd0e 100644 --- a/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp diff --git a/jobs/syncjob.h b/lib/jobs/syncjob.h index 919060be..919060be 100644 --- a/jobs/syncjob.h +++ b/lib/jobs/syncjob.h diff --git a/jobs/{{base}}.cpp.mustache b/lib/jobs/{{base}}.cpp.mustache index b8532c51..b8532c51 100644 --- a/jobs/{{base}}.cpp.mustache +++ b/lib/jobs/{{base}}.cpp.mustache diff --git a/jobs/{{base}}.h.mustache b/lib/jobs/{{base}}.h.mustache index 63aa53e7..63aa53e7 100644 --- a/jobs/{{base}}.h.mustache +++ b/lib/jobs/{{base}}.h.mustache diff --git a/joinstate.h b/lib/joinstate.h index 42613895..42613895 100644 --- a/joinstate.h +++ b/lib/joinstate.h diff --git a/logging.cpp b/lib/logging.cpp index 7476781f..7476781f 100644 --- a/logging.cpp +++ b/lib/logging.cpp diff --git a/logging.h b/lib/logging.h index 8dbfdf30..8dbfdf30 100644 --- a/logging.h +++ b/lib/logging.h diff --git a/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 89967a8a..89967a8a 100644 --- a/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp diff --git a/networkaccessmanager.h b/lib/networkaccessmanager.h index ae847582..ae847582 100644 --- a/networkaccessmanager.h +++ b/lib/networkaccessmanager.h diff --git a/networksettings.cpp b/lib/networksettings.cpp index 48bd09f3..48bd09f3 100644 --- a/networksettings.cpp +++ b/lib/networksettings.cpp diff --git a/networksettings.h b/lib/networksettings.h index 83613060..83613060 100644 --- a/networksettings.h +++ b/lib/networksettings.h @@ -632,6 +632,16 @@ void Room::resetHighlightCount() emit highlightCountChanged(this); } +bool Room::hasAccountData(const QString& type) const +{ + return d->accountData.contains(type); +} + +QVariantHash Room::accountData(const QString& type) const +{ + return d->accountData.value(type); +} + QStringList Room::tagNames() const { return d->tags.keys(); @@ -1644,6 +1654,7 @@ void Room::processAccountDataEvent(EventPtr event) default: d->accountData[event->jsonType()] = event->contentJson().toVariantHash(); + emit accountDataChanged(event->jsonType()); } } @@ -259,6 +259,18 @@ namespace QMatrixClient Q_INVOKABLE int highlightCount() const; Q_INVOKABLE void resetHighlightCount(); + /** Check whether the room has account data of the given type + * Tags and read markers are not supported by this method _yet_. + */ + bool hasAccountData(const QString& type) const; + + /** Get a generic account data event of the given type + * This returns a generic hashmap for any room account data event + * stored on the server. Tags and read markers cannot be retrieved + * using this method _yet_. + */ + QVariantHash accountData(const QString& type) const; + QStringList tagNames() const; TagsMap tags() const; TagRecord tag(const QString& name) const; @@ -377,6 +389,7 @@ namespace QMatrixClient void readMarkerMoved(); void unreadMessagesChanged(Room* room); + void accountDataChanged(QString type); void tagsChanged(); void replacedEvent(const RoomEvent* newEvent, diff --git a/settings.cpp b/lib/settings.cpp index bf369c58..852e19cb 100644 --- a/settings.cpp +++ b/lib/settings.cpp @@ -1,123 +1,123 @@ -#include "settings.h"
-
-#include "logging.h"
-
-#include <QtCore/QUrl>
-
-using namespace QMatrixClient;
-
-QString Settings::legacyOrganizationName {};
-QString Settings::legacyApplicationName {};
-
-void Settings::setLegacyNames(const QString& organizationName,
- const QString& applicationName)
-{
- legacyOrganizationName = organizationName;
- legacyApplicationName = applicationName;
-}
-
-void Settings::setValue(const QString& key, const QVariant& value)
-{
-// qCDebug() << "Setting" << key << "to" << value;
- QSettings::setValue(key, value);
- if (legacySettings.contains(key))
- legacySettings.remove(key);
-}
-
-QVariant Settings::value(const QString& key, const QVariant& defaultValue) const
-{
- auto value = QSettings::value(key, legacySettings.value(key, defaultValue));
- // QML's Qt.labs.Settings stores boolean values as strings, which, if loaded
- // through the usual QSettings interface, confuses QML
- // (QVariant("false") == true in JavaScript). Since we have a mixed
- // environment where both QSettings and Qt.labs.Settings may potentially
- // work with same settings, better ensure compatibility.
- return value.toString() == QStringLiteral("false") ? QVariant(false) : value;
-}
-
-bool Settings::contains(const QString& key) const
-{
- return QSettings::contains(key) || legacySettings.contains(key);
-}
-
-QStringList Settings::childGroups() const
-{
- auto l = QSettings::childGroups();
- return !l.isEmpty() ? l : legacySettings.childGroups();
-}
-
-void SettingsGroup::setValue(const QString& key, const QVariant& value)
-{
- Settings::setValue(groupPath + '/' + key, value);
-}
-
-bool SettingsGroup::contains(const QString& key) const
-{
- return Settings::contains(groupPath + '/' + key);
-}
-
-QVariant SettingsGroup::value(const QString& key, const QVariant& defaultValue) const
-{
- return Settings::value(groupPath + '/' + key, defaultValue);
-}
-
-QString SettingsGroup::group() const
-{
- return groupPath;
-}
-
-QStringList SettingsGroup::childGroups() const
-{
- const_cast<SettingsGroup*>(this)->beginGroup(groupPath);
- const_cast<QSettings&>(legacySettings).beginGroup(groupPath);
- QStringList l = Settings::childGroups();
- const_cast<SettingsGroup*>(this)->endGroup();
- const_cast<QSettings&>(legacySettings).endGroup();
- return l;
-}
-
-void SettingsGroup::remove(const QString& key)
-{
- QString fullKey { groupPath };
- if (!key.isEmpty())
- fullKey += "/" + key;
- Settings::remove(fullKey);
-}
-
-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
-{
- return QUrl::fromUserInput(value("homeserver").toString());
-}
-
-void AccountSettings::setHomeserver(const QUrl& url)
-{
- setValue("homeserver", url.toString());
-}
-
-QString AccountSettings::userId() const
-{
- return group().section('/', -1);
-}
-
-QString AccountSettings::accessToken() const
-{
- return value("access_token").toString();
-}
-
-void AccountSettings::setAccessToken(const QString& accessToken)
-{
- qCWarning(MAIN) << "Saving access_token to QSettings is insecure."
- " Developers, please save access_token separately.";
- setValue("access_token", accessToken);
-}
-
-void AccountSettings::clearAccessToken()
-{
- legacySettings.remove("access_token");
- legacySettings.remove("device_id"); // Force the server to re-issue it
- remove("access_token");
-}
+#include "settings.h" + +#include "logging.h" + +#include <QtCore/QUrl> + +using namespace QMatrixClient; + +QString Settings::legacyOrganizationName {}; +QString Settings::legacyApplicationName {}; + +void Settings::setLegacyNames(const QString& organizationName, + const QString& applicationName) +{ + legacyOrganizationName = organizationName; + legacyApplicationName = applicationName; +} + +void Settings::setValue(const QString& key, const QVariant& value) +{ +// qCDebug() << "Setting" << key << "to" << value; + QSettings::setValue(key, value); + if (legacySettings.contains(key)) + legacySettings.remove(key); +} + +QVariant Settings::value(const QString& key, const QVariant& defaultValue) const +{ + auto value = QSettings::value(key, legacySettings.value(key, defaultValue)); + // QML's Qt.labs.Settings stores boolean values as strings, which, if loaded + // through the usual QSettings interface, confuses QML + // (QVariant("false") == true in JavaScript). Since we have a mixed + // environment where both QSettings and Qt.labs.Settings may potentially + // work with same settings, better ensure compatibility. + return value.toString() == QStringLiteral("false") ? QVariant(false) : value; +} + +bool Settings::contains(const QString& key) const +{ + return QSettings::contains(key) || legacySettings.contains(key); +} + +QStringList Settings::childGroups() const +{ + auto l = QSettings::childGroups(); + return !l.isEmpty() ? l : legacySettings.childGroups(); +} + +void SettingsGroup::setValue(const QString& key, const QVariant& value) +{ + Settings::setValue(groupPath + '/' + key, value); +} + +bool SettingsGroup::contains(const QString& key) const +{ + return Settings::contains(groupPath + '/' + key); +} + +QVariant SettingsGroup::value(const QString& key, const QVariant& defaultValue) const +{ + return Settings::value(groupPath + '/' + key, defaultValue); +} + +QString SettingsGroup::group() const +{ + return groupPath; +} + +QStringList SettingsGroup::childGroups() const +{ + const_cast<SettingsGroup*>(this)->beginGroup(groupPath); + const_cast<QSettings&>(legacySettings).beginGroup(groupPath); + QStringList l = Settings::childGroups(); + const_cast<SettingsGroup*>(this)->endGroup(); + const_cast<QSettings&>(legacySettings).endGroup(); + return l; +} + +void SettingsGroup::remove(const QString& key) +{ + QString fullKey { groupPath }; + if (!key.isEmpty()) + fullKey += "/" + key; + Settings::remove(fullKey); +} + +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 +{ + return QUrl::fromUserInput(value("homeserver").toString()); +} + +void AccountSettings::setHomeserver(const QUrl& url) +{ + setValue("homeserver", url.toString()); +} + +QString AccountSettings::userId() const +{ + return group().section('/', -1); +} + +QString AccountSettings::accessToken() const +{ + return value("access_token").toString(); +} + +void AccountSettings::setAccessToken(const QString& accessToken) +{ + qCWarning(MAIN) << "Saving access_token to QSettings is insecure." + " Developers, please save access_token separately."; + setValue("access_token", accessToken); +} + +void AccountSettings::clearAccessToken() +{ + legacySettings.remove("access_token"); + legacySettings.remove("device_id"); // Force the server to re-issue it + remove("access_token"); +} diff --git a/settings.h b/lib/settings.h index 27ec9ba5..27ec9ba5 100644 --- a/settings.h +++ b/lib/settings.h @@ -265,6 +265,7 @@ void User::rename(const QString& newName, const Room* r) qCWarning(MAIN) << "Passing a null room to two-argument User::rename()" "is incorrect; client developer, please fix it"; rename(newName); + return; } Q_ASSERT_X(r->memberJoinState(this) == JoinState::Join, __FUNCTION__, "Attempt to rename a user that's not a room member"); diff --git a/lib/util.h b/lib/util.h new file mode 100644 index 00000000..92198b0b --- /dev/null +++ b/lib/util.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * Copyright (C) 2016 Kitsune Ral <kitsune-ral@users.sf.net> + * + * 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 <QtCore/QMetaEnum> +#include <QtCore/QDebug> + +#include <functional> +#include <memory> + +namespace QMatrixClient +{ + // The below enables pretty-printing of enums in logs +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) +#define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) +#else + // Thanks to Olivier for spelling it and for making Q_ENUM to replace it: + // https://woboq.com/blog/q_enum.html +#define REGISTER_ENUM(EnumName) \ + Q_ENUMS(EnumName) \ + friend QDebug operator<<(QDebug dbg, EnumName val) \ + { \ + static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ + return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey(int(val)); \ + } +#endif + + template <typename T1, typename PtrT2> + inline auto unique_ptr_cast(PtrT2&& p) + { + return std::unique_ptr<T1>(static_cast<T1*>(p.release())); + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + // Copy-pasted from Qt 5.10 + template <typename T> + Q_DECL_CONSTEXPR typename std::add_const<T>::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } + // prevent rvalue arguments: + template <typename T> + static void qAsConst(const T &&) Q_DECL_EQ_DELETE; +#endif +} // namespace QMatrixClient + diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 144c9dbc..edba623e 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -1,5 +1,5 @@ QT += network -CONFIG += c++14 warn_on rtti_off +CONFIG += c++14 warn_on rtti_off create_prl win32-msvc* { QMAKE_CXXFLAGS_WARN_ON += -wd4100 @@ -7,72 +7,73 @@ win32-msvc* { QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter } -INCLUDEPATH += $$PWD +SRCPATH = $$PWD/lib +INCLUDEPATH += $$SRCPATH HEADERS += \ - $$PWD/connectiondata.h \ - $$PWD/connection.h \ - $$PWD/room.h \ - $$PWD/user.h \ - $$PWD/avatar.h \ - $$PWD/util.h \ - $$PWD/events/event.h \ - $$PWD/events/eventcontent.h \ - $$PWD/events/roommessageevent.h \ - $$PWD/events/simplestateevents.h \ - $$PWD/events/roommemberevent.h \ - $$PWD/events/roomavatarevent.h \ - $$PWD/events/typingevent.h \ - $$PWD/events/receiptevent.h \ - $$PWD/events/accountdataevents.h \ - $$PWD/events/directchatevent.h \ - $$PWD/events/redactionevent.h \ - $$PWD/jobs/requestdata.h \ - $$PWD/jobs/basejob.h \ - $$PWD/jobs/checkauthmethods.h \ - $$PWD/jobs/passwordlogin.h \ - $$PWD/jobs/sendeventjob.h \ - $$PWD/jobs/postreceiptjob.h \ - $$PWD/jobs/joinroomjob.h \ - $$PWD/jobs/roommessagesjob.h \ - $$PWD/jobs/syncjob.h \ - $$PWD/jobs/mediathumbnailjob.h \ - $$PWD/jobs/setroomstatejob.h \ - $$files($$PWD/jobs/generated/*.h, false) \ - $$PWD/logging.h \ - $$PWD/settings.h \ - $$PWD/networksettings.h \ - $$PWD/networkaccessmanager.h \ - $$PWD/jobs/downloadfilejob.h \ - $$PWD/jobs/postreadmarkersjob.h + $$SRCPATH/connectiondata.h \ + $$SRCPATH/connection.h \ + $$SRCPATH/room.h \ + $$SRCPATH/user.h \ + $$SRCPATH/avatar.h \ + $$SRCPATH/util.h \ + $$SRCPATH/events/event.h \ + $$SRCPATH/events/eventcontent.h \ + $$SRCPATH/events/roommessageevent.h \ + $$SRCPATH/events/simplestateevents.h \ + $$SRCPATH/events/roommemberevent.h \ + $$SRCPATH/events/roomavatarevent.h \ + $$SRCPATH/events/typingevent.h \ + $$SRCPATH/events/receiptevent.h \ + $$SRCPATH/events/accountdataevents.h \ + $$SRCPATH/events/directchatevent.h \ + $$SRCPATH/events/redactionevent.h \ + $$SRCPATH/jobs/requestdata.h \ + $$SRCPATH/jobs/basejob.h \ + $$SRCPATH/jobs/checkauthmethods.h \ + $$SRCPATH/jobs/passwordlogin.h \ + $$SRCPATH/jobs/sendeventjob.h \ + $$SRCPATH/jobs/postreceiptjob.h \ + $$SRCPATH/jobs/joinroomjob.h \ + $$SRCPATH/jobs/roommessagesjob.h \ + $$SRCPATH/jobs/syncjob.h \ + $$SRCPATH/jobs/mediathumbnailjob.h \ + $$SRCPATH/jobs/setroomstatejob.h \ + $$SRCPATH/jobs/downloadfilejob.h \ + $$SRCPATH/jobs/postreadmarkersjob.h \ + $$files($$SRCPATH/jobs/generated/*.h, false) \ + $$SRCPATH/logging.h \ + $$SRCPATH/settings.h \ + $$SRCPATH/networksettings.h \ + $$SRCPATH/networkaccessmanager.h SOURCES += \ - $$PWD/connectiondata.cpp \ - $$PWD/connection.cpp \ - $$PWD/room.cpp \ - $$PWD/user.cpp \ - $$PWD/avatar.cpp \ - $$PWD/events/event.cpp \ - $$PWD/events/eventcontent.cpp \ - $$PWD/events/roommessageevent.cpp \ - $$PWD/events/roommemberevent.cpp \ - $$PWD/events/typingevent.cpp \ - $$PWD/events/receiptevent.cpp \ - $$PWD/events/directchatevent.cpp \ - $$PWD/jobs/requestdata.cpp \ - $$PWD/jobs/basejob.cpp \ - $$PWD/jobs/checkauthmethods.cpp \ - $$PWD/jobs/passwordlogin.cpp \ - $$PWD/jobs/sendeventjob.cpp \ - $$PWD/jobs/postreceiptjob.cpp \ - $$PWD/jobs/joinroomjob.cpp \ - $$PWD/jobs/roommessagesjob.cpp \ - $$PWD/jobs/syncjob.cpp \ - $$PWD/jobs/mediathumbnailjob.cpp \ - $$PWD/jobs/setroomstatejob.cpp \ - $$files($$PWD/jobs/generated/*.cpp, false) \ - $$PWD/logging.cpp \ - $$PWD/settings.cpp \ - $$PWD/networksettings.cpp \ - $$PWD/networkaccessmanager.cpp \ - $$PWD/jobs/downloadfilejob.cpp + $$SRCPATH/connectiondata.cpp \ + $$SRCPATH/connection.cpp \ + $$SRCPATH/room.cpp \ + $$SRCPATH/user.cpp \ + $$SRCPATH/avatar.cpp \ + $$SRCPATH/events/event.cpp \ + $$SRCPATH/events/eventcontent.cpp \ + $$SRCPATH/events/roommessageevent.cpp \ + $$SRCPATH/events/roommemberevent.cpp \ + $$SRCPATH/events/typingevent.cpp \ + $$SRCPATH/events/receiptevent.cpp \ + $$SRCPATH/events/directchatevent.cpp \ + $$SRCPATH/jobs/requestdata.cpp \ + $$SRCPATH/jobs/basejob.cpp \ + $$SRCPATH/jobs/checkauthmethods.cpp \ + $$SRCPATH/jobs/passwordlogin.cpp \ + $$SRCPATH/jobs/sendeventjob.cpp \ + $$SRCPATH/jobs/postreceiptjob.cpp \ + $$SRCPATH/jobs/joinroomjob.cpp \ + $$SRCPATH/jobs/roommessagesjob.cpp \ + $$SRCPATH/jobs/syncjob.cpp \ + $$SRCPATH/jobs/mediathumbnailjob.cpp \ + $$SRCPATH/jobs/setroomstatejob.cpp \ + $$SRCPATH/jobs/downloadfilejob.cpp \ + $$files($$SRCPATH/jobs/generated/*.cpp, false) \ + $$SRCPATH/logging.cpp \ + $$SRCPATH/settings.cpp \ + $$SRCPATH/networksettings.cpp \ + $$SRCPATH/networkaccessmanager.cpp diff --git a/state.cpp b/state.cpp deleted file mode 100644 index 59a3b007..00000000 --- a/state.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de> - * - * 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 "state.h" - -#include "events/event.h" - -using namespace QMatrixClient; - -class State::Private -{ - public: - Event* event; - QString stateKey; - QString replacesState; -}; - - -State::State(Event* event) - : d(new Private) -{ - d->event = event; -} - -State::~State() -{ - delete d; -} - -Event* State::event() const -{ - return d->event; -} - -QString State::stateKey() const -{ - return d->stateKey; -} - -QString State::replacesState() const -{ - return d->replacesState; -} - -State* State::fromJson(const QJsonObject& obj) -{ - Event* event = Event::fromJson(obj); - if( !event ) - return nullptr; - State* state = new State(event); - state->d->stateKey = obj.value("state_key").toString(); - state->d->replacesState = obj.value("replaces_state").toString(); - return state; -} diff --git a/state.h b/state.h deleted file mode 100644 index b059ea92..00000000 --- a/state.h +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de> - * - * 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 <QtCore/QString> -#include <QtCore/QJsonObject> - -namespace QMatrixClient -{ - class Event; - - /** - * Wraps an event that is a state - */ - class State - { - public: - State(Event* event); - virtual ~State(); - - Event* event() const; - QString stateKey() const; - QString replacesState() const; - - static State* fromJson(const QJsonObject& obj); - - private: - class Private; - Private* d; - }; -} diff --git a/util.h b/util.h deleted file mode 100644 index 65de0610..00000000 --- a/util.h +++ /dev/null @@ -1,205 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2016 Kitsune Ral <kitsune-ral@users.sf.net> - * - * 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 <QtCore/QMetaEnum> -#include <QtCore/QDebug> - -#include <functional> - -namespace QMatrixClient -{ - /** - * @brief Lookup a value by a key in a varargs list - * - * This function template takes the value of its first argument (selector) - * as a key and searches for it in the key-value map passed in - * a parameter pack (every next pair of arguments forms a key-value pair). - * If a match is found, the respective value is returned; if no pairs - * matched, the last value (fallback) is returned. - * - * All options should be of the same type or implicitly castable to the - * type of the first option. If you need some specific type to cast to - * you can explicitly provide it as the ValueT template parameter - * (e.g. <code>lookup<void*>(parameters...)</code>). Note that pointers - * to methods of different classes and even to functions with different - * signatures are of different types. If their return types are castable - * to some common one, @see dispatch that deals with this by swallowing - * the method invocation. - * - * Below is an example of usage to select a parser depending on contents of - * a JSON object: - * {@code - * auto parser = lookup(obj.value["type"].toString(), - * "type1", fn1, - * "type2", fn2, - * fallbackFn); - * parser(obj); - * } - * - * The implementation is based on tail recursion; every recursion step - * removes 2 arguments (match and value). There's no selector value for the - * fallback option (the last one); therefore, the total number of lookup() - * arguments should be even: selector + n key-value pairs + fallback - * - * @note Beware of calling lookup() with a <code>const char*</code> selector - * (the first parameter) - most likely it won't do what you expect because - * of shallow comparison. - */ - template <typename ValueT, typename SelectorT> - ValueT lookup(SelectorT/*unused*/, ValueT&& fallback) - { - return std::forward<ValueT>(fallback); - } - - template <typename ValueT, typename SelectorT, typename KeyT, typename... Ts> - ValueT lookup(SelectorT&& selector, KeyT&& key, ValueT&& value, Ts&&... remainder) - { - if( selector == key ) - return std::forward<ValueT>(value); - - // Drop the failed key-value pair and recurse with 2 arguments less. - return lookup<ValueT>(std::forward<SelectorT>(selector), - std::forward<Ts>(remainder)...); - } - - /** - * A wrapper around lookup() for functions of different types castable - * to a common std::function<> form - * - * This class uses std::function<> magic to first capture arguments of - * a yet-unknown function or function object, and then to coerce types of - * all functions/function objects passed for lookup to the type - * std::function<ResultT(ArgTs...). Without Dispatch<>, you would have - * to pass the specific function type to lookup, since your functions have - * different signatures. The type is not always obvious, and the resulting - * construct in client code would almost always be rather cumbersome. - * Dispatch<> deduces the necessary function type (well, almost - you still - * have to specify the result type) and hides the clumsiness. For more - * information on what std::function<> can wrap around, see - * https://cpptruths.blogspot.jp/2015/11/covariance-and-contravariance-in-c.html - * - * The function arguments are captured by value (i.e. copied) to avoid - * hard-to-find issues with dangling references in cases when a Dispatch<> - * object is passed across different contexts (e.g. returned from another - * function). - * - * \tparam ResultT - the desired type of a picked function invocation (mandatory) - * \tparam ArgTs - function argument types (deduced) - */ -#if __GNUC__ < 5 && __GNUC_MINOR__ < 9 - // GCC 4.8 cannot cope with parameter packs inside lambdas; so provide a single - // argument version of Dispatch<> that we only need so far. - template <typename ResultT, typename ArgT> -#else - template <typename ResultT, typename... ArgTs> -#endif - class Dispatch - { - // The implementation takes a chapter from functional programming: - // Dispatch<> uses a function that in turn accepts a function as its - // argument. The sole purpose of the outer function (initialized by - // a lambda-expression in the constructor) is to store the arguments - // to any of the functions later looked up. The inner function (its - // type is defined by fn_t alias) is the one returned by lookup() - // invocation inside to(). - // - // It's a bit counterintuitive to specify function parameters before - // the list of functions but otherwise it would take several overloads - // here to match all the ways a function-like behaviour can be done: - // reference-to-function, pointer-to-function, function object. This - // probably could be done as well but I preferred a more compact - // solution: you show what you have and if it's possible to bring all - // your functions to the same std::function<> based on what you have - // as parameters, the code will compile. If it's not possible, modern - // compilers are already good enough at pinpointing a specific place - // where types don't match. - public: -#if __GNUC__ < 5 && __GNUC_MINOR__ < 9 - using fn_t = std::function<ResultT(ArgT)>; - explicit Dispatch(ArgT&& arg) - : boundArgs([=](fn_t &&f) { return f(std::move(arg)); }) - { } -#else - using fn_t = std::function<ResultT(ArgTs...)>; - explicit Dispatch(ArgTs&&... args) - : boundArgs([=](fn_t &&f) { return f(std::move(args)...); }) - { } -#endif - - template <typename... LookupParamTs> - ResultT to(LookupParamTs&&... lookupParams) - { - // Here's the magic, two pieces of it: - // 1. Specifying fn_t in lookup() wraps all functions in - // \p lookupParams into the same std::function<> type. This - // includes conversion of return types from more specific to more - // generic (because std::function is covariant by return types and - // contravariant by argument types (see the link in the Doxygen - // part of the comments). - auto fn = lookup<fn_t>(std::forward<LookupParamTs>(lookupParams)...); - // 2. Passing the result of lookup() to boundArgs() invokes the - // lambda-expression mentioned in the constructor, which simply - // invokes this passed function with a set of arguments captured - // by lambda. - if (fn) - return boundArgs(std::move(fn)); - - // A shortcut to allow passing nullptr for a function; - // a default-constructed ResultT will be returned - // (for pointers, it will be nullptr) - return {}; - } - - private: - std::function<ResultT(fn_t&&)> boundArgs; - }; - - /** - * Dispatch a set of parameters to one of a set of functions, depending on - * a selector value - * - * Use <code>dispatch<CommonType>(parameters).to(lookup parameters)</code> - * instead of lookup() if you need to pick one of several functions returning - * types castable to the same CommonType. See event.cpp for a typical use case. - * - * \see Dispatch - */ - template <typename ResultT, typename... ArgTs> - Dispatch<ResultT, ArgTs...> dispatch(ArgTs&& ... args) - { - return Dispatch<ResultT, ArgTs...>(std::forward<ArgTs>(args)...); - } - - // The below enables pretty-printing of enums in logs -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) -#define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) -#else - // Thanks to Olivier for spelling it and for making Q_ENUM to replace it: - // https://woboq.com/blog/q_enum.html -#define REGISTER_ENUM(EnumName) \ - Q_ENUMS(EnumName) \ - friend QDebug operator<<(QDebug dbg, EnumName val) \ - { \ - static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ - return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey(int(val)); \ - } -#endif -} // namespace QMatrixClient - |