forked from blue/squawk
Compare commits
No commits in common. "master" and "newMessageFeedPreview1" have entirely different histories.
master
...
newMessage
55
CHANGELOG.md
55
CHANGELOG.md
@ -1,70 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Squawk 0.2.2 (May 05, 2022)
|
## Squawk 0.2.0 (Unreleased)
|
||||||
### Bug fixes
|
|
||||||
- now when you remove an account it actually gets removed
|
|
||||||
- segfault on unitialized Availability in some rare occesions
|
|
||||||
- fixed crash when you open a dialog with someone that has only error messages in archive
|
|
||||||
- message height is now calculated correctly on Chinese and Japanese paragraphs
|
|
||||||
- the app doesn't crash on SIGINT anymore
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
- there is a way to disable an account and it wouldn't connect when you change availability
|
|
||||||
- if you cancel password query an account becomes inactive and doesn't annoy you anymore
|
|
||||||
- if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password
|
|
||||||
- if left the password field empty and chose KWallet as a storage Squawk will try to get that passord from KWallet before asking you to input it
|
|
||||||
- accounts now connect to the server asyncronously - if one is stopped on password prompt another is connecting
|
|
||||||
- actualized translations, added English localization file
|
|
||||||
|
|
||||||
### New features
|
|
||||||
- new "About" window with links, license, gratitudes
|
|
||||||
- if the authentication failed Squawk will ask againg for your password and login
|
|
||||||
- now there is an amount of unread messages showing on top of Squawk launcher icon
|
|
||||||
- notifications now have buttons to open a conversation or to mark that message as read
|
|
||||||
|
|
||||||
## Squawk 0.2.1 (Apr 02, 2022)
|
|
||||||
### Bug fixes
|
|
||||||
- build in release mode now no longer spams warnings
|
|
||||||
- build now correctly installs all build plugin libs
|
|
||||||
- a bug where the correction message was received, the indication was on but the text didn't actually change
|
|
||||||
- message body now doesn't intecept context menu from the whole message
|
|
||||||
- message input now doesn't increase font when you remove everything from it
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
- reduced amount of places where platform specific path separator is used
|
|
||||||
- now message input is automatically focused when you open a dialog or a room
|
|
||||||
- what() method on unhandled exception now actually tells what happened
|
|
||||||
|
|
||||||
### New features
|
|
||||||
- the settings are here! You con config different stuff from there
|
|
||||||
- now it's possible to set up different qt styles from settings
|
|
||||||
- if you have KConfig nad KConfigWidgets packages installed - you can choose from global color schemes
|
|
||||||
- it's possible now to choose a folder where squawk is going to store downloaded files
|
|
||||||
- now you can correct your message
|
|
||||||
|
|
||||||
## Squawk 0.2.0 (Jan 10, 2022)
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- carbon copies switches on again after reconnection
|
- carbon copies switches on again after reconnection
|
||||||
- requesting the history of the current chat after reconnection
|
- requesting the history of the current chat after reconnection
|
||||||
- global availability (in drop down list) gets restored after reconnection
|
- global availability (in drop down list) gets restored after reconnection
|
||||||
- status icon in active chat changes when presence of the pen pal changes
|
- status icon in active chat changes when presence of the pen pal changes
|
||||||
- infinite progress when open the dialogue with something that has no history to show
|
- infinite progress when open the dialogue with something that has no history to show
|
||||||
- fallback icons for buttons, when no supported theme is installed (shunf4)
|
|
||||||
- better handling messages with no id (shunf4)
|
|
||||||
- removed dependency: uuid, now it's on Qt (shunf4)
|
|
||||||
- better requesting latest history (shunf4)
|
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- slightly reduced the traffic on the startup by not requesting history of all MUCs
|
- slightly reduced the traffic on the startup by not requesting history of all MUCs
|
||||||
- completely rewritten message feed, now it works way faster and looks cooler
|
- completely rewritten message feed, now it works way faster
|
||||||
- OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager
|
- OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager
|
||||||
- show in folder now is supposed to try it's best to show file in folder, even you don't have KIO installed
|
- show in folder now is supposed to try it's best to show file in folder, even you don't have KIO installed
|
||||||
- once uploaded local files don't get second time uploaded - the remote URL is reused
|
- once uploaded local files don't get second time uploaded - the remote URL is reused
|
||||||
- way better compilation time (vae)
|
|
||||||
|
|
||||||
### New features
|
|
||||||
- pasting images from clipboard to attachment (shunf4)
|
|
||||||
- possible compilation for windows and macOS (shunf4)
|
|
||||||
|
|
||||||
## Squawk 0.1.5 (Jul 29, 2020)
|
## Squawk 0.1.5 (Jul 29, 2020)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
266
CMakeLists.txt
266
CMakeLists.txt
@ -1,8 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.4)
|
||||||
project(squawk VERSION 0.2.2 LANGUAGES CXX)
|
project(squawk)
|
||||||
|
|
||||||
cmake_policy(SET CMP0076 NEW)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
cmake_policy(SET CMP0079 NEW)
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
@ -10,164 +9,127 @@ set(CMAKE_AUTOUIC ON)
|
|||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
include_directories(.)
|
||||||
|
|
||||||
set(WIN32_FLAG "")
|
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||||
set(MACOSX_BUNDLE_FLAG "")
|
find_package(Qt5LinguistTools)
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
if (WIN32)
|
|
||||||
set(WIN32_FLAG WIN32)
|
|
||||||
endif(WIN32)
|
|
||||||
if (APPLE)
|
|
||||||
set(MACOSX_BUNDLE_FLAG MACOSX_BUNDLE)
|
|
||||||
endif(APPLE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(squawk ${WIN32_FLAG} ${MACOSX_BUNDLE_FLAG})
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
|
||||||
|
|
||||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
|
||||||
option(WITH_KWALLET "Build KWallet support module" ON)
|
|
||||||
option(WITH_KIO "Build KIO support module" ON)
|
|
||||||
option(WITH_KCONFIG "Build KConfig support module" ON)
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
## Qt
|
|
||||||
set(QT_VERSION_MAJOR 5)
|
|
||||||
find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
|
|
||||||
find_package(Boost COMPONENTS)
|
|
||||||
|
|
||||||
target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Widgets_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5DBus_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Gui_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Xml_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Network_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Core_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
## QXmpp
|
|
||||||
if (SYSTEM_QXMPP)
|
|
||||||
find_package(QXmpp CONFIG)
|
|
||||||
|
|
||||||
if (NOT QXmpp_FOUND)
|
|
||||||
set(SYSTEM_QXMPP OFF)
|
|
||||||
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
|
||||||
else ()
|
|
||||||
message("Building with system QXmpp")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (NOT SYSTEM_QXMPP)
|
|
||||||
target_link_libraries(squawk PRIVATE qxmpp)
|
|
||||||
add_subdirectory(external/qxmpp)
|
|
||||||
else ()
|
|
||||||
target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
## KIO
|
|
||||||
if (WITH_KIO)
|
|
||||||
find_package(KF5KIO CONFIG)
|
|
||||||
|
|
||||||
if (NOT KF5KIO_FOUND)
|
|
||||||
set(WITH_KIO OFF)
|
|
||||||
message("KIO package wasn't found, KIO support modules wouldn't be built")
|
|
||||||
else ()
|
|
||||||
target_compile_definitions(squawk PRIVATE WITH_KIO)
|
|
||||||
message("Building with support of KIO")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
## KWallet
|
|
||||||
if (WITH_KWALLET)
|
|
||||||
find_package(KF5Wallet CONFIG)
|
|
||||||
|
|
||||||
if (NOT KF5Wallet_FOUND)
|
|
||||||
set(WITH_KWALLET OFF)
|
|
||||||
message("KWallet package wasn't found, KWallet support module wouldn't be built")
|
|
||||||
else ()
|
|
||||||
target_compile_definitions(squawk PRIVATE WITH_KWALLET)
|
|
||||||
message("Building with support of KWallet")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (WITH_KCONFIG)
|
|
||||||
find_package(KF5Config CONFIG)
|
|
||||||
if (NOT KF5Config_FOUND)
|
|
||||||
set(WITH_KCONFIG OFF)
|
|
||||||
message("KConfig package wasn't found, KConfig support modules wouldn't be built")
|
|
||||||
else()
|
|
||||||
find_package(KF5ConfigWidgets CONFIG)
|
|
||||||
if (NOT KF5ConfigWidgets_FOUND)
|
|
||||||
set(WITH_KCONFIG OFF)
|
|
||||||
message("KConfigWidgets package wasn't found, KConfigWidgets support modules wouldn't be built")
|
|
||||||
else()
|
|
||||||
target_compile_definitions(squawk PRIVATE WITH_KCONFIG)
|
|
||||||
message("Building with support of KConfig")
|
|
||||||
message("Building with support of KConfigWidgets")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
## Signal (TODO)
|
|
||||||
# find_package(Signal REQUIRED)
|
|
||||||
|
|
||||||
## LMDB
|
|
||||||
find_package(LMDB REQUIRED)
|
|
||||||
|
|
||||||
# Linking
|
|
||||||
target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
|
|
||||||
target_link_libraries(squawk PRIVATE lmdb)
|
|
||||||
target_link_libraries(squawk PRIVATE simpleCrypt)
|
|
||||||
# Link thread libraries on Linux
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
target_link_libraries(squawk PRIVATE Threads::Threads)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Build type
|
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set (COMPILE_OPTIONS -fno-sized-deallocation) # for eliminating _ZdlPvm
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
list(APPEND COMPILE_OPTIONS -O3)
|
|
||||||
endif()
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
|
||||||
list(APPEND COMPILE_OPTIONS -g)
|
|
||||||
list(APPEND COMPILE_OPTIONS -Wall)
|
|
||||||
list(APPEND COMPILE_OPTIONS -Wextra)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message("Compilation options: " ${COMPILE_OPTIONS})
|
set(squawk_SRC
|
||||||
target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS})
|
main.cpp
|
||||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
exception.cpp
|
||||||
|
signalcatcher.cpp
|
||||||
|
shared/global.cpp
|
||||||
|
shared/utils.cpp
|
||||||
|
shared/message.cpp
|
||||||
|
shared/vcard.cpp
|
||||||
|
shared/icons.cpp
|
||||||
|
shared/messageinfo.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(squawk_HEAD
|
||||||
|
exception.h
|
||||||
|
signalcatcher.h
|
||||||
|
shared.h
|
||||||
|
shared/enums.h
|
||||||
|
shared/message.h
|
||||||
|
shared/global.h
|
||||||
|
shared/utils.h
|
||||||
|
shared/vcard.h
|
||||||
|
shared/icons.h
|
||||||
|
shared/messageinfo.h
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
|
||||||
|
execute_process(COMMAND convert -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
configure_file(packaging/squawk.desktop squawk.desktop COPYONLY)
|
||||||
|
|
||||||
|
set(TS_FILES
|
||||||
|
translations/squawk.ru.ts
|
||||||
|
)
|
||||||
|
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||||
|
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
||||||
|
|
||||||
|
qt5_add_resources(RCC resources/resources.qrc)
|
||||||
|
|
||||||
|
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||||
|
option(WITH_KWALLET "Build KWallet support module" ON)
|
||||||
|
option(WITH_KIO "Build KIO support module" ON)
|
||||||
|
|
||||||
|
if (SYSTEM_QXMPP)
|
||||||
|
find_package(QXmpp CONFIG)
|
||||||
|
|
||||||
|
if (NOT QXmpp_FOUND)
|
||||||
|
set(SYSTEM_QXMPP OFF)
|
||||||
|
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
||||||
|
else()
|
||||||
|
message("Building with system QXmpp")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT SYSTEM_QXMPP)
|
||||||
|
add_subdirectory(external/qxmpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_KWALLET)
|
||||||
|
find_package(KF5Wallet CONFIG)
|
||||||
|
|
||||||
|
if (NOT KF5Wallet_FOUND)
|
||||||
|
set(WITH_KWALLET OFF)
|
||||||
|
message("KWallet package wasn't found, KWallet support module wouldn't be built")
|
||||||
|
else()
|
||||||
|
add_definitions(-DWITH_KWALLET)
|
||||||
|
message("Building with support of KWallet")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
|
||||||
|
target_link_libraries(squawk Qt5::Widgets)
|
||||||
|
|
||||||
|
if (WITH_KIO)
|
||||||
|
find_package(KF5KIO CONFIG)
|
||||||
|
|
||||||
|
if (NOT KF5KIO_FOUND)
|
||||||
|
set(WITH_KIO OFF)
|
||||||
|
message("KIO package wasn't found, KIO support modules wouldn't be built")
|
||||||
|
else()
|
||||||
|
add_definitions(-DWITH_KIO)
|
||||||
|
message("Building with support of KIO")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(main)
|
|
||||||
add_subdirectory(core)
|
|
||||||
add_subdirectory(external/simpleCrypt)
|
|
||||||
add_subdirectory(packaging)
|
|
||||||
add_subdirectory(plugins)
|
|
||||||
add_subdirectory(resources)
|
|
||||||
add_subdirectory(shared)
|
|
||||||
add_subdirectory(translations)
|
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(plugins)
|
||||||
|
|
||||||
|
add_subdirectory(external/simpleCrypt)
|
||||||
|
|
||||||
|
target_link_libraries(squawk squawkUI)
|
||||||
|
target_link_libraries(squawk squawkCORE)
|
||||||
|
target_link_libraries(squawk uuid)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
||||||
|
|
||||||
# Install the executable
|
# Install the executable
|
||||||
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
|
||||||
install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
||||||
if (APPLE)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
||||||
add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..."
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
||||||
COMMAND "${Qt5Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app"
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||||
)
|
|
||||||
endif(APPLE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
@ -595,17 +595,17 @@ pointer to where the full notice is found.
|
|||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
Copyright (C) <year> <name of author>
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
36
README.md
36
README.md
@ -4,20 +4,17 @@
|
|||||||
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
||||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||||
|
|
||||||
![Squawk screenshot](https://macaw.me/images/squawk/0.2.2.png)
|
![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png)
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- QT 5.12 *(lower versions might work but it wasn't tested)*
|
- QT 5.12 *(lower versions might work but it wasn't tested)*
|
||||||
|
- uuid _(usually included in some other package, for example it's ***libutil-linux*** in archlinux)_
|
||||||
- lmdb
|
- lmdb
|
||||||
- CMake 3.4 or higher
|
- CMake 3.0 or higher
|
||||||
- qxmpp 1.1.0 or higher
|
- qxmpp 1.1.0 or higher
|
||||||
- KDE Frameworks: kwallet (optional)
|
- KDE Frameworks: kwallet (optional)
|
||||||
- KDE Frameworks: KIO (optional)
|
- KDE Frameworks: KIO (optional)
|
||||||
- KDE Frameworks: KConfig (optional)
|
|
||||||
- KDE Frameworks: KConfigWidgets (optional)
|
|
||||||
- Boost (just one little hpp from there)
|
|
||||||
- Imagemagick (for compilation, to rasterize an SVG logo)
|
|
||||||
|
|
||||||
### Getting
|
### Getting
|
||||||
|
|
||||||
@ -37,26 +34,6 @@ You can also clone the repo and build it from source
|
|||||||
|
|
||||||
Squawk requires Qt with SSL enabled. It uses CMake as build system.
|
Squawk requires Qt with SSL enabled. It uses CMake as build system.
|
||||||
|
|
||||||
Please check the prerequisites and install them before installation.
|
|
||||||
|
|
||||||
#### For Windows (Mingw-w64) build
|
|
||||||
|
|
||||||
You need Qt for mingw64 (MinGW 64-bit) platform when installing Qt.
|
|
||||||
|
|
||||||
The best way to acquire library `lmdb` and `boost` is through Msys2.
|
|
||||||
|
|
||||||
First install Msys2, and then install `mingw-w64-x86_64-lmdb` and `mingw-w64-x86_64-boost` by pacman.
|
|
||||||
|
|
||||||
Then you need to provide the cmake cache entry when calling cmake for configuration:
|
|
||||||
|
|
||||||
```
|
|
||||||
cmake .. -D LMDB_ROOT_DIR:PATH=<Msys2 Mingw64 Root Directory> -D BOOST_ROOT:PATH=<Msys2 Mingw64 Root Directory>
|
|
||||||
```
|
|
||||||
|
|
||||||
`<Msys2 Mingw64 Root Directory>`: e.g. `C:/msys64/mingw64`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
There are two ways to build, it depends whether you have qxmpp installed in your system
|
There are two ways to build, it depends whether you have qxmpp installed in your system
|
||||||
|
|
||||||
#### Building with system qxmpp
|
#### Building with system qxmpp
|
||||||
@ -68,7 +45,7 @@ $ git clone https://git.macaw.me/blue/squawk
|
|||||||
$ cd squawk
|
$ cd squawk
|
||||||
$ mkdir build
|
$ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake .. [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...]
|
$ cmake ..
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -81,12 +58,10 @@ $ git clone --recurse-submodules https://git.macaw.me/blue/squawk
|
|||||||
$ cd squawk
|
$ cd squawk
|
||||||
$ mkdir build
|
$ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake .. -D SYSTEM_QXMPP=False [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...]
|
$ cmake .. -D SYSTEM_QXMPP=False
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
```
|
```
|
||||||
|
|
||||||
You can always refer to `appveyor.yml` to see how AppVeyor build squawk.
|
|
||||||
|
|
||||||
### List of keys
|
### List of keys
|
||||||
|
|
||||||
Here is the list of keys you can pass to configuration phase of `cmake ..`.
|
Here is the list of keys you can pass to configuration phase of `cmake ..`.
|
||||||
@ -94,7 +69,6 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`.
|
|||||||
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
|
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
|
||||||
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
|
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
|
||||||
- `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`)
|
- `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`)
|
||||||
- `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
117
appveyor.yml
117
appveyor.yml
@ -1,117 +0,0 @@
|
|||||||
image:
|
|
||||||
- Visual Studio 2019
|
|
||||||
- "Previous Ubuntu1804"
|
|
||||||
- macOS-Mojave
|
|
||||||
|
|
||||||
branches:
|
|
||||||
except:
|
|
||||||
- gh-pages
|
|
||||||
|
|
||||||
for:
|
|
||||||
-
|
|
||||||
matrix:
|
|
||||||
only:
|
|
||||||
- image: Visual Studio 2019
|
|
||||||
|
|
||||||
environment:
|
|
||||||
QTDIR: C:\Qt\5.15.2\mingw81_64
|
|
||||||
QTTOOLDIR: C:\Qt\Tools\mingw810_64\bin
|
|
||||||
QTNINJADIR: C:\Qt\Tools\Ninja
|
|
||||||
|
|
||||||
install:
|
|
||||||
- set PATH=%QTTOOLDIR%;%QTNINJADIR%;%QTDIR%\bin;%PATH%
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- choco install --yes zstandard
|
|
||||||
- choco install --yes --version=7.1.0.2 imagemagick.app
|
|
||||||
- set PATH=C:\Program Files\ImageMagick-7.1.0-Q16-HDRI;%PATH%
|
|
||||||
|
|
||||||
- mkdir lmdb
|
|
||||||
- cd lmdb
|
|
||||||
- curl -OL https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lmdb-0.9.27-1-any.pkg.tar.zst
|
|
||||||
- zstd -d ./mingw-w64-x86_64-lmdb-0.9.27-1-any.pkg.tar.zst
|
|
||||||
- tar -xvf ./mingw-w64-x86_64-lmdb-0.9.27-1-any.pkg.tar
|
|
||||||
- cd ..
|
|
||||||
|
|
||||||
- mkdir boost
|
|
||||||
- cd boost
|
|
||||||
- curl -OL https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-boost-1.77.0-2-any.pkg.tar.zst
|
|
||||||
- zstd -d ./mingw-w64-x86_64-boost-1.77.0-2-any.pkg.tar.zst
|
|
||||||
- tar -xvf ./mingw-w64-x86_64-boost-1.77.0-2-any.pkg.tar
|
|
||||||
- cd ..
|
|
||||||
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake -GNinja -DCMAKE_BUILD_TYPE:String=Release -DCMAKE_PREFIX_PATH:STRING=%QTDIR% -DLMDB_ROOT_DIR:PATH=C:/projects/squawk/lmdb/mingw64 -DBOOST_ROOT:PATH=C:/projects/squawk/boost/mingw64 ..
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cmake --build .
|
|
||||||
- mkdir deploy
|
|
||||||
- cd deploy
|
|
||||||
- copy ..\squawk.exe .\
|
|
||||||
- copy ..\external\qxmpp\src\libqxmpp.dll .\
|
|
||||||
- windeployqt .\squawk.exe
|
|
||||||
- windeployqt .\libqxmpp.dll
|
|
||||||
- cd ..\..
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: build/deploy/squawk.exe
|
|
||||||
name: Squawk executable (Qt 5.15.2)
|
|
||||||
|
|
||||||
- path: build/deploy
|
|
||||||
name: Squawk deployment with Qt Framework
|
|
||||||
|
|
||||||
-
|
|
||||||
matrix:
|
|
||||||
only:
|
|
||||||
- image: macOS-Mojave
|
|
||||||
|
|
||||||
install:
|
|
||||||
- brew install lmdb imagemagick boost
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake -DCMAKE_BUILD_TYPE:String=Release -DCMAKE_PREFIX_PATH:STRING=$HOME/Qt/5.15.2/clang_64 ..
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cmake --build .
|
|
||||||
|
|
||||||
after_build:
|
|
||||||
- zip -r squawk.app.zip squawk.app
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: build/squawk.app/Contents/MacOS/squawk
|
|
||||||
name: Squawk executable (Qt 5.15.2)
|
|
||||||
- path: build/external/qxmpp/src/
|
|
||||||
name: QXMPP
|
|
||||||
- path: build/squawk.app.zip
|
|
||||||
name: Squawk Bundle with Qt Framework (Qt 5.15.2)
|
|
||||||
|
|
||||||
-
|
|
||||||
matrix:
|
|
||||||
only:
|
|
||||||
- image: "Previous Ubuntu1804"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- ls $HOME/Qt
|
|
||||||
- sudo apt update
|
|
||||||
- sudo apt install -y liblmdb-dev liblmdb0 imagemagick mesa-common-dev libglu1-mesa-dev libboost-all-dev
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake -DCMAKE_BUILD_TYPE:String=Release -DCMAKE_PREFIX_PATH:STRING=$HOME/Qt/5.12.10/gcc_64 -DCMAKE_BUILD_RPATH="\$ORIGIN" ..
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cmake --build .
|
|
||||||
|
|
||||||
after_build:
|
|
||||||
- zip -r squawk.zip squawk -j external/qxmpp/src/libqxmpp*
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: build/squawk.zip
|
|
||||||
name: Squawk executable and libraries (Qt 5.12)
|
|
@ -21,32 +21,27 @@
|
|||||||
# LMDB_INCLUDE_DIRS The location of LMDB headers.
|
# LMDB_INCLUDE_DIRS The location of LMDB headers.
|
||||||
|
|
||||||
find_path(LMDB_ROOT_DIR
|
find_path(LMDB_ROOT_DIR
|
||||||
NAMES include/lmdb.h
|
NAMES include/lmdb.h
|
||||||
)
|
)
|
||||||
|
|
||||||
find_library(LMDB_LIBRARIES
|
find_library(LMDB_LIBRARIES
|
||||||
NAMES liblmdb.a liblmdb.so liblmdb.so.a liblmdb.dll.a # We want lmdb to be static, if possible
|
NAMES lmdb
|
||||||
HINTS ${LMDB_ROOT_DIR}/lib
|
HINTS ${LMDB_ROOT_DIR}/lib
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(lmdb UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(lmdb PROPERTIES
|
|
||||||
IMPORTED_LOCATION ${LMDB_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
find_path(LMDB_INCLUDE_DIRS
|
find_path(LMDB_INCLUDE_DIRS
|
||||||
NAMES lmdb.h
|
NAMES lmdb.h
|
||||||
HINTS ${LMDB_ROOT_DIR}/include
|
HINTS ${LMDB_ROOT_DIR}/include
|
||||||
)
|
)
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(LMDB DEFAULT_MSG
|
find_package_handle_standard_args(LMDB DEFAULT_MSG
|
||||||
LMDB_LIBRARIES
|
LMDB_LIBRARIES
|
||||||
LMDB_INCLUDE_DIRS
|
LMDB_INCLUDE_DIRS
|
||||||
)
|
)
|
||||||
|
|
||||||
mark_as_advanced(
|
mark_as_advanced(
|
||||||
LMDB_ROOT_DIR
|
LMDB_ROOT_DIR
|
||||||
LMDB_LIBRARIES
|
LMDB_LIBRARIES
|
||||||
LMDB_INCLUDE_DIRS
|
LMDB_INCLUDE_DIRS
|
||||||
)
|
)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
find_path(Signal_INCLUDE_DIR NAMES signal/signal_protocol.h)
|
|
||||||
find_library(Signal_LIBRARY signal-protocol-c)
|
|
||||||
mark_as_advanced(Signal_INCLUDE_DIR Signal_LIBRARY)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(Signal REQUIRED_VARS Signal_LIBRARY Signal_INCLUDE_DIR)
|
|
||||||
|
|
||||||
if (Signal_FOUND AND NOT TARGET Signal::Signal)
|
|
||||||
add_library(Signal::Signal UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(Signal::Signal PROPERTIES
|
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
|
||||||
IMPORTED_LOCATION "${Signal_LIBRARY}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${Signal_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
endif ()
|
|
@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
|
||||||
<key>CFBundleGetInfoString</key>
|
|
||||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleLongVersionString</key>
|
|
||||||
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
|
||||||
<key>CSResourcesFileMapped</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
|
||||||
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
<key>NSHighResolutionCapable</key>
|
|
||||||
<string>True</string>
|
|
||||||
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1,29 +1,49 @@
|
|||||||
set(SIGNALCATCHER_SOURCE signalcatcher.cpp)
|
cmake_minimum_required(VERSION 3.3)
|
||||||
if(WIN32)
|
project(squawkCORE)
|
||||||
set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp)
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
target_sources(squawk PRIVATE
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
account.cpp
|
|
||||||
account.h
|
|
||||||
adapterfunctions.cpp
|
|
||||||
adapterfunctions.h
|
|
||||||
conference.cpp
|
|
||||||
conference.h
|
|
||||||
contact.cpp
|
|
||||||
contact.h
|
|
||||||
networkaccess.cpp
|
|
||||||
networkaccess.h
|
|
||||||
rosteritem.cpp
|
|
||||||
rosteritem.h
|
|
||||||
${SIGNALCATCHER_SOURCE}
|
|
||||||
signalcatcher.h
|
|
||||||
squawk.cpp
|
|
||||||
squawk.h
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
|
find_package(Qt5Core CONFIG REQUIRED)
|
||||||
|
find_package(Qt5Gui CONFIG REQUIRED)
|
||||||
|
find_package(Qt5Network CONFIG REQUIRED)
|
||||||
|
find_package(Qt5Xml CONFIG REQUIRED)
|
||||||
|
find_package(LMDB REQUIRED)
|
||||||
|
|
||||||
|
set(squawkCORE_SRC
|
||||||
|
squawk.cpp
|
||||||
|
account.cpp
|
||||||
|
archive.cpp
|
||||||
|
rosteritem.cpp
|
||||||
|
contact.cpp
|
||||||
|
conference.cpp
|
||||||
|
urlstorage.cpp
|
||||||
|
networkaccess.cpp
|
||||||
|
adapterFuctions.cpp
|
||||||
|
handlers/messagehandler.cpp
|
||||||
|
handlers/rosterhandler.cpp
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(handlers)
|
|
||||||
add_subdirectory(storage)
|
|
||||||
add_subdirectory(passwordStorageEngines)
|
add_subdirectory(passwordStorageEngines)
|
||||||
|
|
||||||
|
# Tell CMake to create the helloworld executable
|
||||||
|
add_library(squawkCORE STATIC ${squawkCORE_SRC})
|
||||||
|
|
||||||
|
|
||||||
|
if(SYSTEM_QXMPP)
|
||||||
|
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Use the Widgets module from Qt 5.
|
||||||
|
target_link_libraries(squawkCORE Qt5::Core)
|
||||||
|
target_link_libraries(squawkCORE Qt5::Network)
|
||||||
|
target_link_libraries(squawkCORE Qt5::Gui)
|
||||||
|
target_link_libraries(squawkCORE Qt5::Xml)
|
||||||
|
target_link_libraries(squawkCORE qxmpp)
|
||||||
|
target_link_libraries(squawkCORE lmdb)
|
||||||
|
target_link_libraries(squawkCORE simpleCrypt)
|
||||||
|
if (WITH_KWALLET)
|
||||||
|
target_link_libraries(squawkCORE kwalletPSE)
|
||||||
|
endif()
|
||||||
|
588
core/account.cpp
588
core/account.cpp
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
|
||||||
Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, bool p_active, NetworkAccess* p_net, QObject* parent):
|
Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent):
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
name(p_name),
|
name(p_name),
|
||||||
archiveQueries(),
|
archiveQueries(),
|
||||||
@ -41,15 +41,13 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
rcpm(new QXmppMessageReceiptManager()),
|
rcpm(new QXmppMessageReceiptManager()),
|
||||||
reconnectScheduled(false),
|
reconnectScheduled(false),
|
||||||
reconnectTimer(new QTimer),
|
reconnectTimer(new QTimer),
|
||||||
|
avatarHash(),
|
||||||
|
avatarType(),
|
||||||
|
ownVCardRequestInProgress(false),
|
||||||
network(p_net),
|
network(p_net),
|
||||||
passwordType(Shared::AccountPassword::plain),
|
passwordType(Shared::AccountPassword::plain),
|
||||||
lastError(Error::none),
|
|
||||||
pepSupport(false),
|
|
||||||
active(p_active),
|
|
||||||
notReadyPassword(false),
|
|
||||||
mh(new MessageHandler(this)),
|
mh(new MessageHandler(this)),
|
||||||
rh(new RosterHandler(this)),
|
rh(new RosterHandler(this))
|
||||||
vh(new VCardHandler(this))
|
|
||||||
{
|
{
|
||||||
config.setUser(p_login);
|
config.setUser(p_login);
|
||||||
config.setDomain(p_server);
|
config.setDomain(p_server);
|
||||||
@ -75,6 +73,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
|
|
||||||
client.addExtension(mm);
|
client.addExtension(mm);
|
||||||
client.addExtension(bm);
|
client.addExtension(bm);
|
||||||
|
|
||||||
|
QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived);
|
||||||
|
//QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler
|
||||||
|
|
||||||
client.addExtension(um);
|
client.addExtension(um);
|
||||||
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived);
|
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived);
|
||||||
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed);
|
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed);
|
||||||
@ -89,18 +91,62 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
client.addExtension(rcpm);
|
client.addExtension(rcpm);
|
||||||
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
||||||
|
|
||||||
|
|
||||||
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
path += "/" + name;
|
||||||
|
QDir dir(path);
|
||||||
|
|
||||||
|
if (!dir.exists()) {
|
||||||
|
bool res = dir.mkpath(path);
|
||||||
|
if (!res) {
|
||||||
|
qDebug() << "Couldn't create a cache directory for account" << name;
|
||||||
|
throw 22;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile* avatar = new QFile(path + "/avatar.png");
|
||||||
|
QString type = "png";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.jpg");
|
||||||
|
type = "jpg";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.jpeg");
|
||||||
|
type = "jpeg";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.gif");
|
||||||
|
type = "gif";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatar->exists()) {
|
||||||
|
if (avatar->open(QFile::ReadOnly)) {
|
||||||
|
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||||
|
sha1.addData(avatar);
|
||||||
|
avatarHash = sha1.result();
|
||||||
|
avatarType = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (avatarType.size() != 0) {
|
||||||
|
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
} else {
|
||||||
|
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
||||||
|
}
|
||||||
|
|
||||||
reconnectTimer->setSingleShot(true);
|
reconnectTimer->setSingleShot(true);
|
||||||
QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer);
|
QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer);
|
||||||
|
|
||||||
if (name == "Test") {
|
// QXmppLogger* logger = new QXmppLogger(this);
|
||||||
QXmppLogger* logger = new QXmppLogger(this);
|
// logger->setLoggingType(QXmppLogger::SignalLogging);
|
||||||
logger->setLoggingType(QXmppLogger::SignalLogging);
|
// client.setLogger(logger);
|
||||||
client.setLogger(logger);
|
//
|
||||||
|
// QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
|
||||||
QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
|
// qDebug() << text;
|
||||||
qDebug() << text;
|
// });
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Account::~Account()
|
Account::~Account()
|
||||||
@ -114,7 +160,6 @@ Account::~Account()
|
|||||||
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
||||||
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
||||||
|
|
||||||
delete vh;
|
|
||||||
delete mh;
|
delete mh;
|
||||||
delete rh;
|
delete rh;
|
||||||
|
|
||||||
@ -140,12 +185,7 @@ void Core::Account::connect()
|
|||||||
reconnectTimer->stop();
|
reconnectTimer->stop();
|
||||||
}
|
}
|
||||||
if (state == Shared::ConnectionState::disconnected) {
|
if (state == Shared::ConnectionState::disconnected) {
|
||||||
if (notReadyPassword) {
|
client.connectToServer(config, presence);
|
||||||
emit needPassword();
|
|
||||||
} else {
|
|
||||||
client.connectToServer(config, presence);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qDebug("An attempt to connect an account which is already connected, skipping");
|
qDebug("An attempt to connect an account which is already connected, skipping");
|
||||||
}
|
}
|
||||||
@ -184,7 +224,6 @@ void Core::Account::onClientStateChange(QXmppClient::State st)
|
|||||||
dm->requestItems(getServer());
|
dm->requestItems(getServer());
|
||||||
dm->requestInfo(getServer());
|
dm->requestInfo(getServer());
|
||||||
}
|
}
|
||||||
lastError = Error::none;
|
|
||||||
emit connectionStateChanged(state);
|
emit connectionStateChanged(state);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -216,17 +255,45 @@ void Core::Account::onClientStateChange(QXmppClient::State st)
|
|||||||
|
|
||||||
void Core::Account::reconnect()
|
void Core::Account::reconnect()
|
||||||
{
|
{
|
||||||
if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting
|
if (state == Shared::ConnectionState::connected && !reconnectScheduled) {
|
||||||
if (state == Shared::ConnectionState::connected) {
|
reconnectScheduled = true;
|
||||||
reconnectScheduled = true;
|
reconnectTimer->start(500);
|
||||||
reconnectTimer->start(500);
|
client.disconnectFromServer();
|
||||||
client.disconnectFromServer();
|
} else {
|
||||||
} else {
|
qDebug() << "An attempt to reconnect account" << getName() << "which was not connected";
|
||||||
qDebug() << "An attempt to reconnect account" << getName() << "which was not connected";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Core::Account::getName() const {
|
||||||
|
return name;}
|
||||||
|
|
||||||
|
QString Core::Account::getLogin() const {
|
||||||
|
return config.user();}
|
||||||
|
|
||||||
|
QString Core::Account::getPassword() const {
|
||||||
|
return config.password();}
|
||||||
|
|
||||||
|
QString Core::Account::getServer() const {
|
||||||
|
return config.domain();}
|
||||||
|
|
||||||
|
Shared::AccountPassword Core::Account::getPasswordType() const {
|
||||||
|
return passwordType;}
|
||||||
|
|
||||||
|
void Core::Account::setPasswordType(Shared::AccountPassword pt) {
|
||||||
|
passwordType = pt; }
|
||||||
|
|
||||||
|
void Core::Account::setLogin(const QString& p_login) {
|
||||||
|
config.setUser(p_login);}
|
||||||
|
|
||||||
|
void Core::Account::setName(const QString& p_name) {
|
||||||
|
name = p_name;}
|
||||||
|
|
||||||
|
void Core::Account::setPassword(const QString& p_password) {
|
||||||
|
config.setPassword(p_password);}
|
||||||
|
|
||||||
|
void Core::Account::setServer(const QString& p_server) {
|
||||||
|
config.setDomain(p_server);}
|
||||||
|
|
||||||
Shared::Availability Core::Account::getAvailability() const
|
Shared::Availability Core::Account::getAvailability() const
|
||||||
{
|
{
|
||||||
if (state == Shared::ConnectionState::connected) {
|
if (state == Shared::ConnectionState::connected) {
|
||||||
@ -258,11 +325,32 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
|||||||
QString jid = comps.front().toLower();
|
QString jid = comps.front().toLower();
|
||||||
QString resource = comps.back();
|
QString resource = comps.back();
|
||||||
|
|
||||||
if (jid == getBareJid()) {
|
QString myJid = getLogin() + "@" + getServer();
|
||||||
|
|
||||||
|
if (jid == myJid) {
|
||||||
if (resource == getResource()) {
|
if (resource == getResource()) {
|
||||||
emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType()));
|
emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType()));
|
||||||
} else {
|
} else {
|
||||||
vh->handleOtherPresenceOfMyAccountChange(p_presence);
|
if (!ownVCardRequestInProgress) {
|
||||||
|
switch (p_presence.vCardUpdateType()) {
|
||||||
|
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||||
|
if (avatarHash != p_presence.photoHash()) {
|
||||||
|
vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RosterItem* item = rh->getRosterItem(jid);
|
RosterItem* item = rh->getRosterItem(jid);
|
||||||
@ -304,6 +392,18 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Core::Account::getResource() const {
|
||||||
|
return config.resource();}
|
||||||
|
|
||||||
|
void Core::Account::setResource(const QString& p_resource) {
|
||||||
|
config.setResource(p_resource);}
|
||||||
|
|
||||||
|
QString Core::Account::getFullJid() const {
|
||||||
|
return getLogin() + "@" + getServer() + "/" + getResource();}
|
||||||
|
|
||||||
|
void Core::Account::sendMessage(const Shared::Message& data) {
|
||||||
|
mh->sendMessage(data);}
|
||||||
|
|
||||||
void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg)
|
void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg)
|
||||||
{
|
{
|
||||||
if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) {
|
if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) {
|
||||||
@ -369,14 +469,6 @@ void Core::Account::onContactNeedHistory(const QString& before, const QString& a
|
|||||||
query.setAfter(after);
|
query.setAfter(after);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (before.size() == 0 && after.size() == 0) {
|
|
||||||
// https://xmpp.org/extensions/xep-0313.html#sect-idm46556759682304
|
|
||||||
// To request the page at the end of the archive
|
|
||||||
// (i.e. the most recent messages), include just an
|
|
||||||
// empty <before/> element in the RSM part of the query.
|
|
||||||
// As defined by RSM, this will return the last page of the archive.
|
|
||||||
query.setBefore("");
|
|
||||||
}
|
|
||||||
qDebug() << "Remote query for" << contact->jid << "from" << after << ", to" << before;
|
qDebug() << "Remote query for" << contact->jid << "from" << after << ", to" << before;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +509,6 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
qDebug() << "Error";
|
qDebug() << "Error";
|
||||||
QString errorText;
|
QString errorText;
|
||||||
QString errorType;
|
QString errorType;
|
||||||
lastError = Error::other;
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case QXmppClient::SocketError:
|
case QXmppClient::SocketError:
|
||||||
errorText = client.socketErrorString();
|
errorText = client.socketErrorString();
|
||||||
@ -459,7 +550,6 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
break;
|
break;
|
||||||
case QXmppStanza::Error::NotAuthorized:
|
case QXmppStanza::Error::NotAuthorized:
|
||||||
errorText = "Authentication error";
|
errorText = "Authentication error";
|
||||||
lastError = Error::authentication;
|
|
||||||
break;
|
break;
|
||||||
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
|
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
|
||||||
case QXmppStanza::Error::PaymentRequired:
|
case QXmppStanza::Error::PaymentRequired:
|
||||||
@ -503,9 +593,6 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
errorText = "Policy violation";
|
errorText = "Policy violation";
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
|
||||||
errorText = "Unknown Error";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errorType = "Client stream error";
|
errorType = "Client stream error";
|
||||||
@ -569,166 +656,6 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined)
|
|||||||
conf->setJoined(joined);
|
conf->setJoined(joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
|
|
||||||
{
|
|
||||||
if (items.from() == getServer()) {
|
|
||||||
std::set<QString> needToRequest;
|
|
||||||
qDebug() << "Server items list received for account " << name << ":";
|
|
||||||
for (QXmppDiscoveryIq::Item item : items.items()) {
|
|
||||||
QString jid = item.jid();
|
|
||||||
if (jid != getServer()) {
|
|
||||||
qDebug() << " Node" << jid;
|
|
||||||
needToRequest.insert(jid);
|
|
||||||
} else {
|
|
||||||
qDebug() << " " << item.node().toStdString().c_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString& jid : needToRequest) {
|
|
||||||
dm->requestInfo(jid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
|
|
||||||
{
|
|
||||||
if (info.from() == getServer()) {
|
|
||||||
bool enableCC = false;
|
|
||||||
qDebug() << "Server info received for account" << name;
|
|
||||||
QStringList features = info.features();
|
|
||||||
qDebug() << "List of supported features of the server " << getServer() << ":";
|
|
||||||
for (const QString& feature : features) {
|
|
||||||
qDebug() << " " << feature.toStdString().c_str();
|
|
||||||
if (feature == "urn:xmpp:carbons:2") {
|
|
||||||
enableCC = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enableCC) {
|
|
||||||
qDebug() << "Enabling carbon copies for account" << name;
|
|
||||||
cm->setCarbonsEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Requesting account" << name << "capabilities";
|
|
||||||
dm->requestInfo(getBareJid());
|
|
||||||
} else if (info.from() == getBareJid()) {
|
|
||||||
qDebug() << "Received capabilities for account" << name << ":";
|
|
||||||
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
|
|
||||||
bool pepSupported = false;
|
|
||||||
for (const QXmppDiscoveryIq::Identity& identity : identities) {
|
|
||||||
QString type = identity.type();
|
|
||||||
qDebug() << " " << identity.category() << type;
|
|
||||||
if (type == "pep") {
|
|
||||||
pepSupported = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rh->setPepSupport(pepSupported);
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received info for account" << name << "about" << info.from();
|
|
||||||
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
|
|
||||||
for (const QXmppDiscoveryIq::Identity& identity : identities) {
|
|
||||||
qDebug() << " " << identity.name() << identity.category() << identity.type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::handleDisconnection()
|
|
||||||
{
|
|
||||||
cm->setCarbonsEnabled(false);
|
|
||||||
rh->handleOffline();
|
|
||||||
vh->handleOffline();
|
|
||||||
archiveQueries.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
|
|
||||||
{
|
|
||||||
RosterItem* contact = static_cast<RosterItem*>(sender());
|
|
||||||
|
|
||||||
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
|
||||||
if (last) {
|
|
||||||
qDebug() << "The response contains the first accounted message";
|
|
||||||
}
|
|
||||||
emit responseArchive(contact->jid, list, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Account::getActive() const {
|
|
||||||
return active;}
|
|
||||||
|
|
||||||
void Core::Account::setActive(bool p_active) {
|
|
||||||
if (active != p_active) {
|
|
||||||
active = p_active;
|
|
||||||
|
|
||||||
emit changed({
|
|
||||||
{"active", active}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Account::getResource() const {
|
|
||||||
return config.resource();}
|
|
||||||
|
|
||||||
void Core::Account::setResource(const QString& p_resource) {
|
|
||||||
config.setResource(p_resource);}
|
|
||||||
|
|
||||||
QString Core::Account::getBareJid() const {
|
|
||||||
return getLogin() + "@" + getServer();}
|
|
||||||
|
|
||||||
QString Core::Account::getFullJid() const {
|
|
||||||
return getBareJid() + "/" + getResource();}
|
|
||||||
|
|
||||||
QString Core::Account::getName() const {
|
|
||||||
return name;}
|
|
||||||
|
|
||||||
QString Core::Account::getLogin() const {
|
|
||||||
return config.user();}
|
|
||||||
|
|
||||||
QString Core::Account::getPassword() const {
|
|
||||||
return config.password();}
|
|
||||||
|
|
||||||
QString Core::Account::getServer() const {
|
|
||||||
return config.domain();}
|
|
||||||
|
|
||||||
Shared::AccountPassword Core::Account::getPasswordType() const {
|
|
||||||
return passwordType;}
|
|
||||||
|
|
||||||
void Core::Account::setPasswordType(Shared::AccountPassword pt) {
|
|
||||||
passwordType = pt; }
|
|
||||||
|
|
||||||
void Core::Account::setLogin(const QString& p_login) {
|
|
||||||
config.setUser(p_login);}
|
|
||||||
|
|
||||||
void Core::Account::setName(const QString& p_name) {
|
|
||||||
name = p_name;}
|
|
||||||
|
|
||||||
void Core::Account::setPassword(const QString& p_password) {
|
|
||||||
config.setPassword(p_password);
|
|
||||||
notReadyPassword = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::setServer(const QString& p_server) {
|
|
||||||
config.setDomain(p_server);}
|
|
||||||
|
|
||||||
void Core::Account::sendMessage(const Shared::Message& data) {
|
|
||||||
mh->sendMessage(data);}
|
|
||||||
|
|
||||||
void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data){
|
|
||||||
mh->requestChangeMessage(jid, messageId, data);}
|
|
||||||
|
|
||||||
void Core::Account::resendMessage(const QString& jid, const QString& id) {
|
|
||||||
mh->resendMessage(jid, id);}
|
|
||||||
|
|
||||||
void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) {
|
|
||||||
mh->sendMessage(data, false, originalId);}
|
|
||||||
|
|
||||||
void Core::Account::requestVCard(const QString& jid) {
|
|
||||||
vh->requestVCard(jid);}
|
|
||||||
|
|
||||||
void Core::Account::uploadVCard(const Shared::VCard& card) {
|
|
||||||
vh->uploadVCard(card);}
|
|
||||||
|
|
||||||
QString Core::Account::getAvatarPath() const {
|
|
||||||
return vh->getAvatarPath();}
|
|
||||||
|
|
||||||
void Core::Account::removeRoomRequest(const QString& jid){
|
void Core::Account::removeRoomRequest(const QString& jid){
|
||||||
rh->removeRoomRequest(jid);}
|
rh->removeRoomRequest(jid);}
|
||||||
|
|
||||||
@ -751,9 +678,248 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Account::invalidatePassword() {
|
void Core::Account::onVCardReceived(const QXmppVCardIq& card)
|
||||||
notReadyPassword = true;}
|
{
|
||||||
|
QString id = card.from();
|
||||||
|
QStringList comps = id.split("/");
|
||||||
|
QString jid = comps.front().toLower();
|
||||||
|
QString resource("");
|
||||||
|
if (comps.size() > 1) {
|
||||||
|
resource = comps.back();
|
||||||
|
}
|
||||||
|
pendingVCardRequests.erase(id);
|
||||||
|
RosterItem* item = rh->getRosterItem(jid);
|
||||||
|
|
||||||
|
if (item == 0) {
|
||||||
|
if (jid == getLogin() + "@" + getServer()) {
|
||||||
|
onOwnVCardReceived(card);
|
||||||
|
} else {
|
||||||
|
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::VCard vCard = item->handleResponseVCard(card, resource);
|
||||||
|
|
||||||
|
emit receivedVCard(jid, vCard);
|
||||||
|
}
|
||||||
|
|
||||||
Core::Account::Error Core::Account::getLastError() const {
|
void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card)
|
||||||
return lastError;}
|
{
|
||||||
|
QByteArray ava = card.photo();
|
||||||
|
bool avaChanged = false;
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/";
|
||||||
|
if (ava.size() > 0) {
|
||||||
|
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||||
|
sha1.addData(ava);
|
||||||
|
QString newHash(sha1.result());
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType newType = db.mimeTypeForData(ava);
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
if (avatarHash != newHash) {
|
||||||
|
QString oldPath = path + "avatar." + avatarType;
|
||||||
|
QFile oldAvatar(oldPath);
|
||||||
|
bool oldToRemove = false;
|
||||||
|
if (oldAvatar.exists()) {
|
||||||
|
if (oldAvatar.rename(oldPath + ".bak")) {
|
||||||
|
oldToRemove = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||||
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
newAvatar.write(ava);
|
||||||
|
newAvatar.close();
|
||||||
|
avatarHash = newHash;
|
||||||
|
avatarType = newType.preferredSuffix();
|
||||||
|
avaChanged = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << name << "but can't save it";
|
||||||
|
if (oldToRemove) {
|
||||||
|
qDebug() << "rolling back to the old avatar";
|
||||||
|
if (!oldAvatar.rename(oldPath)) {
|
||||||
|
qDebug() << "Couldn't roll back to the old avatar in account" << name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||||
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
newAvatar.write(ava);
|
||||||
|
newAvatar.close();
|
||||||
|
avatarHash = newHash;
|
||||||
|
avatarType = newType.preferredSuffix();
|
||||||
|
avaChanged = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << name << "but can't save it";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oldAvatar(path + "avatar." + avatarType);
|
||||||
|
if (!oldAvatar.remove()) {
|
||||||
|
qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing";
|
||||||
|
} else {
|
||||||
|
avatarType = "";
|
||||||
|
avatarHash = "";
|
||||||
|
avaChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avaChanged) {
|
||||||
|
QMap<QString, QVariant> change;
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
change.insert("avatarPath", path + "avatar." + avatarType);
|
||||||
|
} else {
|
||||||
|
presence.setPhotoHash("");
|
||||||
|
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
|
||||||
|
change.insert("avatarPath", "");
|
||||||
|
}
|
||||||
|
client.setClientPresence(presence);
|
||||||
|
emit changed(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
ownVCardRequestInProgress = false;
|
||||||
|
|
||||||
|
Shared::VCard vCard;
|
||||||
|
initializeVCard(vCard, card);
|
||||||
|
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
vCard.setAvatarType(Shared::Avatar::valid);
|
||||||
|
vCard.setAvatarPath(path + "avatar." + avatarType);
|
||||||
|
} else {
|
||||||
|
vCard.setAvatarType(Shared::Avatar::empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit receivedVCard(getLogin() + "@" + getServer(), vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::Account::getAvatarPath() const
|
||||||
|
{
|
||||||
|
if (avatarType.size() == 0) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::requestVCard(const QString& jid)
|
||||||
|
{
|
||||||
|
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||||
|
qDebug() << "requesting vCard" << jid;
|
||||||
|
if (jid == getLogin() + "@" + getServer()) {
|
||||||
|
if (!ownVCardRequestInProgress) {
|
||||||
|
vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vm->requestVCard(jid);
|
||||||
|
pendingVCardRequests.insert(jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::uploadVCard(const Shared::VCard& card)
|
||||||
|
{
|
||||||
|
QXmppVCardIq iq;
|
||||||
|
initializeQXmppVCard(iq, card);
|
||||||
|
|
||||||
|
bool avatarChanged = false;
|
||||||
|
if (card.getAvatarType() != Shared::Avatar::empty) {
|
||||||
|
QString newPath = card.getAvatarPath();
|
||||||
|
QString oldPath = getAvatarPath();
|
||||||
|
QByteArray data;
|
||||||
|
QString type;
|
||||||
|
if (newPath != oldPath) {
|
||||||
|
QFile avatar(newPath);
|
||||||
|
if (!avatar.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "An attempt to upload new vCard to account" << name
|
||||||
|
<< "but it wasn't possible to read file" << newPath
|
||||||
|
<< "which was supposed to be new avatar, uploading old avatar";
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oA(oldPath);
|
||||||
|
if (!oA.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
|
||||||
|
} else {
|
||||||
|
data = oA.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = avatar.readAll();
|
||||||
|
avatarChanged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oA(oldPath);
|
||||||
|
if (!oA.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
|
||||||
|
} else {
|
||||||
|
data = oA.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.size() > 0) {
|
||||||
|
QMimeDatabase db;
|
||||||
|
type = db.mimeTypeForData(data).name();
|
||||||
|
iq.setPhoto(data);
|
||||||
|
iq.setPhotoType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->setClientVCard(iq);
|
||||||
|
onOwnVCardReceived(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
|
||||||
|
{
|
||||||
|
for (QXmppDiscoveryIq::Item item : items.items()) {
|
||||||
|
if (item.jid() != getServer()) {
|
||||||
|
dm->requestInfo(item.jid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
|
||||||
|
{
|
||||||
|
qDebug() << "Discovery info received for account" << name;
|
||||||
|
if (info.from() == getServer()) {
|
||||||
|
if (info.features().contains("urn:xmpp:carbons:2")) {
|
||||||
|
qDebug() << "Enabling carbon copies for account" << name;
|
||||||
|
cm->setCarbonsEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::handleDisconnection()
|
||||||
|
{
|
||||||
|
cm->setCarbonsEnabled(false);
|
||||||
|
rh->handleOffline();
|
||||||
|
archiveQueries.clear();
|
||||||
|
pendingVCardRequests.clear();
|
||||||
|
Shared::VCard vCard; //just to show, that there is now more pending request
|
||||||
|
for (const QString& jid : pendingVCardRequests) {
|
||||||
|
emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error
|
||||||
|
}
|
||||||
|
pendingVCardRequests.clear();
|
||||||
|
ownVCardRequestInProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
|
||||||
|
{
|
||||||
|
RosterItem* contact = static_cast<RosterItem*>(sender());
|
||||||
|
|
||||||
|
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
||||||
|
if (last) {
|
||||||
|
qDebug() << "The response contains the first accounted message";
|
||||||
|
}
|
||||||
|
emit responseArchive(contact->jid, list, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data){
|
||||||
|
mh->requestChangeMessage(jid, messageId, data);}
|
||||||
|
@ -39,17 +39,17 @@
|
|||||||
#include <QXmppBookmarkManager.h>
|
#include <QXmppBookmarkManager.h>
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
#include <QXmppUploadRequestManager.h>
|
#include <QXmppUploadRequestManager.h>
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
#include <QXmppVCardManager.h>
|
#include <QXmppVCardManager.h>
|
||||||
#include <QXmppMessageReceiptManager.h>
|
#include <QXmppMessageReceiptManager.h>
|
||||||
|
|
||||||
#include "shared/shared.h"
|
#include "shared.h"
|
||||||
#include "contact.h"
|
#include "contact.h"
|
||||||
#include "conference.h"
|
#include "conference.h"
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
|
||||||
#include "handlers/messagehandler.h"
|
#include "handlers/messagehandler.h"
|
||||||
#include "handlers/rosterhandler.h"
|
#include "handlers/rosterhandler.h"
|
||||||
#include "handlers/vcardhandler.h"
|
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
@ -59,20 +59,12 @@ class Account : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
friend class MessageHandler;
|
friend class MessageHandler;
|
||||||
friend class RosterHandler;
|
friend class RosterHandler;
|
||||||
friend class VCardHandler;
|
|
||||||
public:
|
public:
|
||||||
enum class Error {
|
|
||||||
authentication,
|
|
||||||
other,
|
|
||||||
none
|
|
||||||
};
|
|
||||||
|
|
||||||
Account(
|
Account(
|
||||||
const QString& p_login,
|
const QString& p_login,
|
||||||
const QString& p_server,
|
const QString& p_server,
|
||||||
const QString& p_password,
|
const QString& p_password,
|
||||||
const QString& p_name,
|
const QString& p_name,
|
||||||
bool p_active,
|
|
||||||
NetworkAccess* p_net,
|
NetworkAccess* p_net,
|
||||||
QObject* parent = 0);
|
QObject* parent = 0);
|
||||||
~Account();
|
~Account();
|
||||||
@ -84,12 +76,8 @@ public:
|
|||||||
QString getPassword() const;
|
QString getPassword() const;
|
||||||
QString getResource() const;
|
QString getResource() const;
|
||||||
QString getAvatarPath() const;
|
QString getAvatarPath() const;
|
||||||
QString getBareJid() const;
|
|
||||||
QString getFullJid() const;
|
|
||||||
Shared::Availability getAvailability() const;
|
Shared::Availability getAvailability() const;
|
||||||
Shared::AccountPassword getPasswordType() const;
|
Shared::AccountPassword getPasswordType() const;
|
||||||
Error getLastError() const;
|
|
||||||
bool getActive() const;
|
|
||||||
|
|
||||||
void setName(const QString& p_name);
|
void setName(const QString& p_name);
|
||||||
void setLogin(const QString& p_login);
|
void setLogin(const QString& p_login);
|
||||||
@ -98,8 +86,8 @@ public:
|
|||||||
void setResource(const QString& p_resource);
|
void setResource(const QString& p_resource);
|
||||||
void setAvailability(Shared::Availability avail);
|
void setAvailability(Shared::Availability avail);
|
||||||
void setPasswordType(Shared::AccountPassword pt);
|
void setPasswordType(Shared::AccountPassword pt);
|
||||||
|
QString getFullJid() const;
|
||||||
void sendMessage(const Shared::Message& data);
|
void sendMessage(const Shared::Message& data);
|
||||||
void setActive(bool p_active);
|
|
||||||
void requestArchive(const QString& jid, int count, const QString& before);
|
void requestArchive(const QString& jid, int count, const QString& before);
|
||||||
void subscribeToContact(const QString& jid, const QString& reason);
|
void subscribeToContact(const QString& jid, const QString& reason);
|
||||||
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
||||||
@ -115,9 +103,6 @@ public:
|
|||||||
void removeRoomRequest(const QString& jid);
|
void removeRoomRequest(const QString& jid);
|
||||||
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void uploadVCard(const Shared::VCard& card);
|
void uploadVCard(const Shared::VCard& card);
|
||||||
void resendMessage(const QString& jid, const QString& id);
|
|
||||||
void replaceMessage(const QString& originalId, const Shared::Message& data);
|
|
||||||
void invalidatePassword();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void connect();
|
void connect();
|
||||||
@ -150,7 +135,6 @@ signals:
|
|||||||
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
||||||
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
||||||
void needPassword();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
@ -171,16 +155,16 @@ private:
|
|||||||
bool reconnectScheduled;
|
bool reconnectScheduled;
|
||||||
QTimer* reconnectTimer;
|
QTimer* reconnectTimer;
|
||||||
|
|
||||||
|
std::set<QString> pendingVCardRequests;
|
||||||
|
|
||||||
|
QString avatarHash;
|
||||||
|
QString avatarType;
|
||||||
|
bool ownVCardRequestInProgress;
|
||||||
NetworkAccess* network;
|
NetworkAccess* network;
|
||||||
Shared::AccountPassword passwordType;
|
Shared::AccountPassword passwordType;
|
||||||
Error lastError;
|
|
||||||
bool pepSupport;
|
|
||||||
bool active;
|
|
||||||
bool notReadyPassword;
|
|
||||||
|
|
||||||
MessageHandler* mh;
|
MessageHandler* mh;
|
||||||
RosterHandler* rh;
|
RosterHandler* rh;
|
||||||
VCardHandler* vh;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientStateChange(QXmppClient::State state);
|
void onClientStateChange(QXmppClient::State state);
|
||||||
@ -193,6 +177,9 @@ private slots:
|
|||||||
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
|
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
|
||||||
|
|
||||||
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
||||||
|
|
||||||
|
void onVCardReceived(const QXmppVCardIq& card);
|
||||||
|
void onOwnVCardReceived(const QXmppVCardIq& card);
|
||||||
|
|
||||||
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
||||||
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
||||||
@ -202,6 +189,9 @@ private:
|
|||||||
void handleDisconnection();
|
void handleDisconnection();
|
||||||
void onReconnectTimer();
|
void onReconnectTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
||||||
|
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Squawk messenger.
|
* Squawk messenger.
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -15,8 +15,10 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
#define CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
|
||||||
#include "adapterfunctions.h"
|
#include "account.h"
|
||||||
|
|
||||||
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
||||||
{
|
{
|
||||||
@ -262,10 +264,12 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
|
|||||||
phone.setType(phone.type() | QXmppVCardPhone::Preferred);
|
phone.setType(phone.type() | QXmppVCardPhone::Preferred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const std::pair<const QString, QXmppVCardPhone>& phone : phones) {
|
for (const std::pair<QString, QXmppVCardPhone>& phone : phones) {
|
||||||
phs.push_back(phone.second);
|
phs.push_back(phone.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iq.setEmails(emails);
|
iq.setEmails(emails);
|
||||||
iq.setPhones(phs);
|
iq.setPhones(phs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // CORE_ADAPTER_FUNCTIONS_H
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
|
||||||
#define CORE_ADAPTER_FUNCTIONS_H
|
|
||||||
|
|
||||||
#include <QXmppVCardIq.h>
|
|
||||||
#include <shared/vcard.h>
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
|
|
||||||
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
|
||||||
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif // CORE_ADAPTER_FUNCTIONS_H
|
|
@ -58,21 +58,13 @@ void Core::Archive::open(const QString& account)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, 5);
|
mdb_env_set_maxdbs(environment, 5);
|
||||||
mdb_env_set_mapsize(environment,
|
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
// On Windows, the file is immediately allocated.
|
|
||||||
// So we have to limit the size.
|
|
||||||
80UL * 1024UL * 1024UL
|
|
||||||
#else
|
|
||||||
512UL * 1024UL * 1024UL
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
|
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
|
||||||
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY | MDB_INTEGERDUP | MDB_DUPSORT, &order);
|
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
||||||
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
||||||
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
|
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
|
||||||
mdb_dbi_open(txn, "sid", MDB_CREATE, &sid);
|
mdb_dbi_open(txn, "sid", MDB_CREATE, &sid);
|
||||||
@ -123,7 +115,6 @@ bool Core::Archive::addElement(const Shared::Message& message)
|
|||||||
if (!opened) {
|
if (!opened) {
|
||||||
throw Closed("addElement", jid.toStdString());
|
throw Closed("addElement", jid.toStdString());
|
||||||
}
|
}
|
||||||
qDebug() << "Adding message with id " << message.getId();
|
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||||
message.serialize(ds);
|
message.serialize(ds);
|
||||||
@ -317,9 +308,8 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString qsid = msg.getStanzaId();
|
if (msg.getStanzaId().size() > 0 && (idChange || !hadStanzaId)) {
|
||||||
if (qsid.size() > 0 && (idChange || !hadStanzaId)) {
|
const std::string& szid = msg.getStanzaId().toStdString();
|
||||||
std::string szid = qsid.toStdString();
|
|
||||||
|
|
||||||
lmdbData.mv_size = szid.size();
|
lmdbData.mv_size = szid.size();
|
||||||
lmdbData.mv_data = (char*)szid.c_str();
|
lmdbData.mv_data = (char*)szid.c_str();
|
@ -25,7 +25,7 @@
|
|||||||
#include <QMimeType>
|
#include <QMimeType>
|
||||||
|
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/exception.h"
|
#include "exception.h"
|
||||||
#include <lmdb.h>
|
#include <lmdb.h>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
@ -356,7 +356,7 @@ Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
|
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
|
||||||
{
|
{
|
||||||
QMap<QString, QVariant> result;
|
QMap<QString, QVariant> result;
|
||||||
for (const std::pair<const QString, Archive::AvatarInfo>& pair : exParticipants) {
|
for (const std::pair<QString, Archive::AvatarInfo>& pair : exParticipants) {
|
||||||
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
|
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
target_sources(squawk PRIVATE
|
|
||||||
messagehandler.cpp
|
|
||||||
messagehandler.h
|
|
||||||
rosterhandler.cpp
|
|
||||||
rosterhandler.h
|
|
||||||
vcardhandler.cpp
|
|
||||||
vcardhandler.h
|
|
||||||
)
|
|
@ -41,10 +41,10 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg)
|
|||||||
handled = handleGroupMessage(msg);
|
handled = handleGroupMessage(msg);
|
||||||
break;
|
break;
|
||||||
case QXmppMessage::Error: {
|
case QXmppMessage::Error: {
|
||||||
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id());
|
QString id = msg.id();
|
||||||
if (std::get<0>(ids)) {
|
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
||||||
QString id = std::get<1>(ids);
|
if (itr != pendingStateMessages.end()) {
|
||||||
QString jid = std::get<2>(ids);
|
QString jid = itr->second;
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
QMap<QString, QVariant> cData = {
|
QMap<QString, QVariant> cData = {
|
||||||
{"state", static_cast<uint>(Shared::Message::State::error)},
|
{"state", static_cast<uint>(Shared::Message::State::error)},
|
||||||
@ -53,7 +53,9 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg)
|
|||||||
if (cnt != 0) {
|
if (cnt != 0) {
|
||||||
cnt->changeMessage(id, cData);
|
cnt->changeMessage(id, cData);
|
||||||
}
|
}
|
||||||
|
;
|
||||||
emit acc->changeMessage(jid, id, cData);
|
emit acc->changeMessage(jid, id, cData);
|
||||||
|
pendingStateMessages.erase(itr);
|
||||||
handled = true;
|
handled = true;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
|
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
|
||||||
@ -71,14 +73,14 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg)
|
|||||||
|
|
||||||
bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
|
bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
|
||||||
{
|
{
|
||||||
if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) {
|
const QString& body(msg.body());
|
||||||
|
if (body.size() != 0) {
|
||||||
Shared::Message sMsg(Shared::Message::chat);
|
Shared::Message sMsg(Shared::Message::chat);
|
||||||
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
||||||
QString jid = sMsg.getPenPalJid();
|
QString jid = sMsg.getPenPalJid();
|
||||||
Contact* cnt = acc->rh->getContact(jid);
|
Contact* cnt = acc->rh->getContact(jid);
|
||||||
if (cnt == 0) {
|
if (cnt == 0) {
|
||||||
cnt = acc->rh->addOutOfRosterContact(jid);
|
cnt = acc->rh->addOutOfRosterContact(jid);
|
||||||
qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact";
|
|
||||||
}
|
}
|
||||||
if (outgoing) {
|
if (outgoing) {
|
||||||
if (forwarded) {
|
if (forwarded) {
|
||||||
@ -109,6 +111,7 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg
|
|||||||
{
|
{
|
||||||
const QString& body(msg.body());
|
const QString& body(msg.body());
|
||||||
if (body.size() != 0) {
|
if (body.size() != 0) {
|
||||||
|
QString id = msg.id();
|
||||||
|
|
||||||
Shared::Message sMsg(Shared::Message::groupChat);
|
Shared::Message sMsg(Shared::Message::groupChat);
|
||||||
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
||||||
@ -118,11 +121,12 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id());
|
std::map<QString, QString>::const_iterator pItr = pendingStateMessages.find(id);
|
||||||
if (std::get<0>(ids)) {
|
if (pItr != pendingStateMessages.end()) {
|
||||||
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
||||||
cnt->changeMessage(std::get<1>(ids), cData);
|
cnt->changeMessage(id, cData);
|
||||||
emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData);
|
pendingStateMessages.erase(pItr);
|
||||||
|
emit acc->changeMessage(jid, id, cData);
|
||||||
} else {
|
} else {
|
||||||
QString oId = msg.replaceId();
|
QString oId = msg.replaceId();
|
||||||
if (oId.size() > 0) {
|
if (oId.size() > 0) {
|
||||||
@ -159,7 +163,6 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
id = source.id();
|
id = source.id();
|
||||||
}
|
}
|
||||||
target.setStanzaId(source.stanzaId());
|
target.setStanzaId(source.stanzaId());
|
||||||
qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId();
|
|
||||||
#else
|
#else
|
||||||
id = source.id();
|
id = source.id();
|
||||||
#endif
|
#endif
|
||||||
@ -168,7 +171,6 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
if (messageId.size() == 0) {
|
if (messageId.size() == 0) {
|
||||||
target.generateRandomId(); //TODO out of desperation, I need at least a random ID
|
target.generateRandomId(); //TODO out of desperation, I need at least a random ID
|
||||||
messageId = target.getId();
|
messageId = target.getId();
|
||||||
qDebug() << "Had do initialize a message with no id, assigning autogenerated" << messageId;
|
|
||||||
}
|
}
|
||||||
target.setFrom(source.from());
|
target.setFrom(source.from());
|
||||||
target.setTo(source.to());
|
target.setTo(source.to());
|
||||||
@ -176,7 +178,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
target.setForwarded(forwarded);
|
target.setForwarded(forwarded);
|
||||||
|
|
||||||
if (guessing) {
|
if (guessing) {
|
||||||
if (target.getFromJid() == acc->getBareJid()) {
|
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
||||||
outgoing = true;
|
outgoing = true;
|
||||||
} else {
|
} else {
|
||||||
outgoing = false;
|
outgoing = false;
|
||||||
@ -223,74 +225,50 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg)
|
|||||||
handleChatMessage(msg, true, true);
|
handleChatMessage(msg, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<bool, QString, QString> Core::MessageHandler::getOriginalPendingMessageId(const QString& id)
|
|
||||||
{
|
|
||||||
std::tuple<bool, QString, QString> result({false, "", ""});
|
|
||||||
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
|
||||||
if (itr != pendingStateMessages.end()) {
|
|
||||||
std::get<0>(result) = true;
|
|
||||||
std::get<2>(result) = itr->second;
|
|
||||||
|
|
||||||
std::map<QString, QString>::const_iterator itrC = pendingCorrectionMessages.find(id);
|
|
||||||
if (itrC != pendingCorrectionMessages.end()) {
|
|
||||||
if (itrC->second.size() > 0) {
|
|
||||||
std::get<1>(result) = itrC->second;
|
|
||||||
} else {
|
|
||||||
std::get<1>(result) = itr->first;
|
|
||||||
}
|
|
||||||
pendingCorrectionMessages.erase(itrC);
|
|
||||||
} else {
|
|
||||||
std::get<1>(result) = itr->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingStateMessages.erase(itr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id)
|
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id)
|
||||||
{
|
{
|
||||||
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(id);
|
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
||||||
if (std::get<0>(ids)) {
|
if (itr != pendingStateMessages.end()) {
|
||||||
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
||||||
RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids));
|
RosterItem* ri = acc->rh->getRosterItem(itr->second);
|
||||||
|
|
||||||
if (ri != 0) {
|
if (ri != 0) {
|
||||||
ri->changeMessage(std::get<1>(ids), cData);
|
ri->changeMessage(id, cData);
|
||||||
}
|
}
|
||||||
emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData);
|
pendingStateMessages.erase(itr);
|
||||||
|
emit acc->changeMessage(itr->second, id, cData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId)
|
void Core::MessageHandler::sendMessage(const Shared::Message& data)
|
||||||
{
|
{
|
||||||
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
||||||
pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId));
|
prepareUpload(data);
|
||||||
prepareUpload(data, newMessage);
|
|
||||||
} else {
|
} else {
|
||||||
performSending(data, originalId, newMessage);
|
performSending(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage)
|
void Core::MessageHandler::performSending(Shared::Message data, bool newMessage)
|
||||||
{
|
{
|
||||||
QString jid = data.getPenPalJid();
|
QString jid = data.getPenPalJid();
|
||||||
QString id = data.getId();
|
QString id = data.getId();
|
||||||
qDebug() << "Sending message with id:" << id;
|
QString oob = data.getOutOfBandUrl();
|
||||||
if (originalId.size() > 0) {
|
|
||||||
qDebug() << "To replace one with id:" << originalId;
|
|
||||||
}
|
|
||||||
RosterItem* ri = acc->rh->getRosterItem(jid);
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
bool sent = false;
|
bool sent = false;
|
||||||
if (newMessage && originalId.size() > 0) {
|
QMap<QString, QVariant> changes;
|
||||||
newMessage = false;
|
|
||||||
}
|
|
||||||
QDateTime sendTime = QDateTime::currentDateTimeUtc();
|
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
QXmppMessage msg(createPacket(data, sendTime, originalId));
|
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
||||||
|
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
||||||
|
msg.setOriginId(id);
|
||||||
|
#endif
|
||||||
|
msg.setId(id);
|
||||||
|
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
||||||
|
msg.setOutOfBandUrl(oob);
|
||||||
|
msg.setReceiptRequested(true);
|
||||||
|
|
||||||
sent = acc->client.sendPacket(msg);
|
sent = acc->client.sendPacket(msg);
|
||||||
|
|
||||||
if (sent) {
|
if (sent) {
|
||||||
data.setState(Shared::Message::State::sent);
|
data.setState(Shared::Message::State::sent);
|
||||||
} else {
|
} else {
|
||||||
@ -303,39 +281,6 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o
|
|||||||
data.setErrorText("You are is offline or reconnecting");
|
data.setErrorText("You are is offline or reconnecting");
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId));
|
|
||||||
|
|
||||||
QString realId;
|
|
||||||
if (originalId.size() > 0) {
|
|
||||||
realId = originalId;
|
|
||||||
} else {
|
|
||||||
realId = id;
|
|
||||||
}
|
|
||||||
if (ri != 0) {
|
|
||||||
if (newMessage) {
|
|
||||||
ri->appendMessageToArchive(data);
|
|
||||||
} else {
|
|
||||||
ri->changeMessage(realId, changes);
|
|
||||||
}
|
|
||||||
if (sent) {
|
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
|
||||||
if (originalId.size() > 0) {
|
|
||||||
pendingCorrectionMessages.insert(std::make_pair(id, originalId));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pendingStateMessages.erase(id);
|
|
||||||
pendingCorrectionMessages.erase(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit acc->changeMessage(jid, realId, changes);
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const
|
|
||||||
{
|
|
||||||
QMap<QString, QVariant> changes;
|
|
||||||
|
|
||||||
QString oob = data.getOutOfBandUrl();
|
|
||||||
Shared::Message::State mstate = data.getState();
|
Shared::Message::State mstate = data.getState();
|
||||||
changes.insert("state", static_cast<uint>(mstate));
|
changes.insert("state", static_cast<uint>(mstate));
|
||||||
if (mstate == Shared::Message::State::error) {
|
if (mstate == Shared::Message::State::error) {
|
||||||
@ -344,51 +289,27 @@ QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data,
|
|||||||
if (oob.size() > 0) {
|
if (oob.size() > 0) {
|
||||||
changes.insert("outOfBandUrl", oob);
|
changes.insert("outOfBandUrl", oob);
|
||||||
}
|
}
|
||||||
if (newMessage) {
|
if (!newMessage) {
|
||||||
data.setTime(time);
|
changes.insert("stamp", data.getTime());
|
||||||
}
|
}
|
||||||
if (originalId.size() > 0) {
|
|
||||||
changes.insert("body", data.getBody());
|
if (ri != 0) {
|
||||||
}
|
if (newMessage) {
|
||||||
changes.insert("stamp", time);
|
ri->appendMessageToArchive(data);
|
||||||
|
} else {
|
||||||
//sometimes (when the image is pasted with ctrl+v)
|
ri->changeMessage(id, changes);
|
||||||
//I start sending message with one path, then copy it to downloads directory
|
}
|
||||||
//so, the final path changes. Let's assume it changes always since it costs me close to nothing
|
if (sent) {
|
||||||
QString attachPath = data.getAttachPath();
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
if (attachPath.size() > 0) {
|
} else {
|
||||||
QString squawkified = Shared::squawkifyPath(attachPath);
|
pendingStateMessages.erase(id);
|
||||||
changes.insert("attachPath", squawkified);
|
|
||||||
if (attachPath != squawkified) {
|
|
||||||
data.setAttachPath(squawkified);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changes;
|
emit acc->changeMessage(jid, id, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const
|
void Core::MessageHandler::prepareUpload(const Shared::Message& data)
|
||||||
{
|
|
||||||
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
|
||||||
QString id(data.getId());
|
|
||||||
|
|
||||||
if (originalId.size() > 0) {
|
|
||||||
msg.setReplaceId(originalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
|
||||||
msg.setOriginId(id);
|
|
||||||
#endif
|
|
||||||
msg.setId(id);
|
|
||||||
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
|
||||||
msg.setOutOfBandUrl(data.getOutOfBandUrl());
|
|
||||||
msg.setReceiptRequested(true);
|
|
||||||
msg.setStamp(time);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage)
|
|
||||||
{
|
{
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
QString jid = data.getPenPalJid();
|
QString jid = data.getPenPalJid();
|
||||||
@ -401,23 +322,16 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe
|
|||||||
QString path = data.getAttachPath();
|
QString path = data.getAttachPath();
|
||||||
QString url = acc->network->getFileRemoteUrl(path);
|
QString url = acc->network->getFileRemoteUrl(path);
|
||||||
if (url.size() != 0) {
|
if (url.size() != 0) {
|
||||||
sendMessageWithLocalUploadedFile(data, url, newMessage);
|
sendMessageWithLocalUploadedFile(data, url);
|
||||||
} else {
|
} else {
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
|
||||||
if (newMessage) {
|
|
||||||
ri->appendMessageToArchive(data);
|
ri->appendMessageToArchive(data);
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
} else {
|
} else {
|
||||||
QMap<QString, QVariant> changes({
|
|
||||||
{"state", (uint)Shared::Message::State::pending}
|
|
||||||
});
|
|
||||||
ri->changeMessage(id, changes);
|
|
||||||
emit acc->changeMessage(jid, id, changes);
|
|
||||||
}
|
|
||||||
//this checks if the file is already uploading, and if so it subscribes to it's success, so, i need to do stuff only if the network knows nothing of this file
|
|
||||||
if (!acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
|
|
||||||
if (acc->um->serviceFound()) {
|
if (acc->um->serviceFound()) {
|
||||||
QFileInfo file(path);
|
QFileInfo file(path);
|
||||||
if (file.exists() && file.isReadable()) {
|
if (file.exists() && file.isReadable()) {
|
||||||
|
ri->appendMessageToArchive(data);
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
uploadingSlotsQueue.emplace_back(path, id);
|
uploadingSlotsQueue.emplace_back(path, id);
|
||||||
if (uploadingSlotsQueue.size() == 1) {
|
if (uploadingSlotsQueue.size() == 1) {
|
||||||
@ -439,6 +353,7 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot)
|
void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot)
|
||||||
{
|
{
|
||||||
if (uploadingSlotsQueue.size() == 0) {
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
@ -505,23 +420,21 @@ void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>&
|
|||||||
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
|
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
|
||||||
{
|
{
|
||||||
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
|
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
|
||||||
pendingStateMessages.erase(messageId);
|
pendingStateMessages.erase(jid);
|
||||||
pendingCorrectionMessages.erase(messageId);
|
|
||||||
requestChangeMessage(jid, messageId, {
|
requestChangeMessage(jid, messageId, {
|
||||||
{"state", static_cast<uint>(Shared::Message::State::error)},
|
{"state", static_cast<uint>(Shared::Message::State::error)},
|
||||||
{"errorText", errorText}
|
{"errorText", errorText}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path)
|
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
if (info.account == acc->getName()) {
|
if (info.account == acc->getName()) {
|
||||||
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
||||||
if (ri != 0) {
|
if (ri != 0) {
|
||||||
Shared::Message msg = ri->getMessage(info.messageId);
|
Shared::Message msg = ri->getMessage(info.messageId);
|
||||||
msg.setAttachPath(path);
|
sendMessageWithLocalUploadedFile(msg, path, false);
|
||||||
sendMessageWithLocalUploadedFile(msg, url, false);
|
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
|
qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
|
||||||
}
|
}
|
||||||
@ -535,11 +448,11 @@ void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg,
|
|||||||
if (msg.getBody().size() == 0) { //not sure why, but most messages do that
|
if (msg.getBody().size() == 0) { //not sure why, but most messages do that
|
||||||
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
|
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
|
||||||
}
|
}
|
||||||
performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage);
|
performSending(msg, newMessage);
|
||||||
//TODO removal/progress update
|
//TODO removal/progress update
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::set<QString> allowedToChangeKeys({
|
static const std::set<QString> allowerToChangeKeys({
|
||||||
"attachPath",
|
"attachPath",
|
||||||
"outOfBandUrl",
|
"outOfBandUrl",
|
||||||
"state",
|
"state",
|
||||||
@ -552,12 +465,12 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin
|
|||||||
if (cnt != 0) {
|
if (cnt != 0) {
|
||||||
bool allSupported = true;
|
bool allSupported = true;
|
||||||
QString unsupportedString;
|
QString unsupportedString;
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
||||||
if (allowedToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
||||||
allSupported = false; //to make a message to look like if it was edited
|
allSupported = false; //to make a message to look like if it was edited
|
||||||
unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method
|
unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method
|
||||||
break; //because the underlying tech assumes that
|
break; //because the underlying tech assumes that the change is initiated by user
|
||||||
} //the change is initiated by user, not by system
|
} //not by system
|
||||||
}
|
}
|
||||||
if (allSupported) {
|
if (allSupported) {
|
||||||
cnt->changeMessage(messageId, data);
|
cnt->changeMessage(messageId, data);
|
||||||
@ -568,28 +481,3 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::resendMessage(const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
|
||||||
if (cnt != 0) {
|
|
||||||
try {
|
|
||||||
Shared::Message msg = cnt->getMessage(id);
|
|
||||||
if (msg.getState() == Shared::Message::State::error) {
|
|
||||||
if (msg.getEdited()){
|
|
||||||
QString originalId = msg.getId();
|
|
||||||
msg.generateRandomId();
|
|
||||||
sendMessage(msg, false, originalId);
|
|
||||||
} else {
|
|
||||||
sendMessage(msg, false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping";
|
|
||||||
}
|
|
||||||
} catch (const Archive::NotFound& err) {
|
|
||||||
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this jid isn't present in account roster, skipping";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
#include <shared/message.h>
|
#include <shared/message.h>
|
||||||
#include <shared/messageinfo.h>
|
#include <shared/messageinfo.h>
|
||||||
#include <shared/pathcheck.h>
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -46,9 +45,8 @@ public:
|
|||||||
MessageHandler(Account* account);
|
MessageHandler(Account* account);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void sendMessage(const Shared::Message& data, bool newMessage = true, QString originalId = "");
|
void sendMessage(const Shared::Message& data);
|
||||||
void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const;
|
void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const;
|
||||||
void resendMessage(const QString& jid, const QString& id);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onMessageReceived(const QXmppMessage& message);
|
void onMessageReceived(const QXmppMessage& message);
|
||||||
@ -58,7 +56,7 @@ public slots:
|
|||||||
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
|
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
|
||||||
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
|
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
|
||||||
void onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
void onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path);
|
void onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
void onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& path, bool up);
|
void onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& path, bool up);
|
||||||
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
@ -67,17 +65,13 @@ private:
|
|||||||
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
||||||
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
|
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
|
||||||
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true);
|
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true);
|
||||||
void performSending(Shared::Message data, const QString& originalId, bool newMessage = true);
|
void performSending(Shared::Message data, bool newMessage = true);
|
||||||
void prepareUpload(const Shared::Message& data, bool newMessage = true);
|
void prepareUpload(const Shared::Message& data);
|
||||||
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
|
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
|
||||||
QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const;
|
|
||||||
QMap<QString, QVariant> getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const;
|
|
||||||
std::tuple<bool, QString, QString> getOriginalPendingMessageId(const QString& id);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Account* acc;
|
Account* acc;
|
||||||
std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
|
std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
|
||||||
std::map<QString, QString> pendingCorrectionMessages; //key is new mesage, value is originalOne
|
|
||||||
std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
|
std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,8 +26,7 @@ Core::RosterHandler::RosterHandler(Core::Account* account):
|
|||||||
conferences(),
|
conferences(),
|
||||||
groups(),
|
groups(),
|
||||||
queuedContacts(),
|
queuedContacts(),
|
||||||
outOfRosterContacts(),
|
outOfRosterContacts()
|
||||||
pepSupport(false)
|
|
||||||
{
|
{
|
||||||
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
||||||
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
||||||
@ -52,7 +51,8 @@ Core::RosterHandler::~RosterHandler()
|
|||||||
|
|
||||||
void Core::RosterHandler::onRosterReceived()
|
void Core::RosterHandler::onRosterReceived()
|
||||||
{
|
{
|
||||||
acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards
|
acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
|
||||||
|
acc->ownVCardRequestInProgress = true;
|
||||||
|
|
||||||
QStringList bj = acc->rm->getRosterBareJids();
|
QStringList bj = acc->rm->getRosterBareJids();
|
||||||
for (int i = 0; i < bj.size(); ++i) {
|
for (int i = 0; i < bj.size(); ++i) {
|
||||||
@ -588,13 +588,4 @@ void Core::RosterHandler::handleOffline()
|
|||||||
pair.second->clearArchiveRequests();
|
pair.second->clearArchiveRequests();
|
||||||
pair.second->downgradeDatabaseState();
|
pair.second->downgradeDatabaseState();
|
||||||
}
|
}
|
||||||
setPepSupport(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Core::RosterHandler::setPepSupport(bool support)
|
|
||||||
{
|
|
||||||
if (pepSupport != support) {
|
|
||||||
pepSupport = support;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,6 @@ public:
|
|||||||
|
|
||||||
void storeConferences();
|
void storeConferences();
|
||||||
void clearConferences();
|
void clearConferences();
|
||||||
void setPepSupport(bool support);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRosterReceived();
|
void onRosterReceived();
|
||||||
@ -108,7 +107,6 @@ private:
|
|||||||
std::map<QString, std::set<QString>> groups;
|
std::map<QString, std::set<QString>> groups;
|
||||||
std::map<QString, QString> queuedContacts;
|
std::map<QString, QString> queuedContacts;
|
||||||
std::set<QString> outOfRosterContacts;
|
std::set<QString> outOfRosterContacts;
|
||||||
bool pepSupport;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,312 +0,0 @@
|
|||||||
// Squawk messenger.
|
|
||||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include "vcardhandler.h"
|
|
||||||
#include "core/account.h"
|
|
||||||
|
|
||||||
Core::VCardHandler::VCardHandler(Account* account):
|
|
||||||
QObject(),
|
|
||||||
acc(account),
|
|
||||||
ownVCardRequestInProgress(false),
|
|
||||||
pendingVCardRequests(),
|
|
||||||
avatarHash(),
|
|
||||||
avatarType()
|
|
||||||
{
|
|
||||||
connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
|
|
||||||
//for some reason it doesn't work, launching from common handler
|
|
||||||
//connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);
|
|
||||||
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
|
||||||
path += "/" + acc->name;
|
|
||||||
QDir dir(path);
|
|
||||||
|
|
||||||
if (!dir.exists()) {
|
|
||||||
bool res = dir.mkpath(path);
|
|
||||||
if (!res) {
|
|
||||||
qDebug() << "Couldn't create a cache directory for account" << acc->name;
|
|
||||||
throw 22;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile* avatar = new QFile(path + "/avatar.png");
|
|
||||||
QString type = "png";
|
|
||||||
if (!avatar->exists()) {
|
|
||||||
delete avatar;
|
|
||||||
avatar = new QFile(path + "/avatar.jpg");
|
|
||||||
type = "jpg";
|
|
||||||
if (!avatar->exists()) {
|
|
||||||
delete avatar;
|
|
||||||
avatar = new QFile(path + "/avatar.jpeg");
|
|
||||||
type = "jpeg";
|
|
||||||
if (!avatar->exists()) {
|
|
||||||
delete avatar;
|
|
||||||
avatar = new QFile(path + "/avatar.gif");
|
|
||||||
type = "gif";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatar->exists()) {
|
|
||||||
if (avatar->open(QFile::ReadOnly)) {
|
|
||||||
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
|
||||||
sha1.addData(avatar);
|
|
||||||
avatarHash = sha1.result();
|
|
||||||
avatarType = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (avatarType.size() != 0) {
|
|
||||||
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
|
||||||
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
|
||||||
} else {
|
|
||||||
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::VCardHandler::~VCardHandler()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card)
|
|
||||||
{
|
|
||||||
QString id = card.from();
|
|
||||||
QStringList comps = id.split("/");
|
|
||||||
QString jid = comps.front().toLower();
|
|
||||||
QString resource("");
|
|
||||||
if (comps.size() > 1) {
|
|
||||||
resource = comps.back();
|
|
||||||
}
|
|
||||||
pendingVCardRequests.erase(id);
|
|
||||||
RosterItem* item = acc->rh->getRosterItem(jid);
|
|
||||||
|
|
||||||
if (item == 0) {
|
|
||||||
if (jid == acc->getBareJid()) {
|
|
||||||
onOwnVCardReceived(card);
|
|
||||||
} else {
|
|
||||||
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::VCard vCard = item->handleResponseVCard(card, resource);
|
|
||||||
|
|
||||||
emit acc->receivedVCard(jid, vCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card)
|
|
||||||
{
|
|
||||||
QByteArray ava = card.photo();
|
|
||||||
bool avaChanged = false;
|
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/";
|
|
||||||
if (ava.size() > 0) {
|
|
||||||
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
|
||||||
sha1.addData(ava);
|
|
||||||
QString newHash(sha1.result());
|
|
||||||
QMimeDatabase db;
|
|
||||||
QMimeType newType = db.mimeTypeForData(ava);
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
if (avatarHash != newHash) {
|
|
||||||
QString oldPath = path + "avatar." + avatarType;
|
|
||||||
QFile oldAvatar(oldPath);
|
|
||||||
bool oldToRemove = false;
|
|
||||||
if (oldAvatar.exists()) {
|
|
||||||
if (oldAvatar.rename(oldPath + ".bak")) {
|
|
||||||
oldToRemove = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
|
||||||
if (newAvatar.open(QFile::WriteOnly)) {
|
|
||||||
newAvatar.write(ava);
|
|
||||||
newAvatar.close();
|
|
||||||
avatarHash = newHash;
|
|
||||||
avatarType = newType.preferredSuffix();
|
|
||||||
avaChanged = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
|
|
||||||
if (oldToRemove) {
|
|
||||||
qDebug() << "rolling back to the old avatar";
|
|
||||||
if (!oldAvatar.rename(oldPath)) {
|
|
||||||
qDebug() << "Couldn't roll back to the old avatar in account" << acc->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
|
||||||
if (newAvatar.open(QFile::WriteOnly)) {
|
|
||||||
newAvatar.write(ava);
|
|
||||||
newAvatar.close();
|
|
||||||
avatarHash = newHash;
|
|
||||||
avatarType = newType.preferredSuffix();
|
|
||||||
avaChanged = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
QFile oldAvatar(path + "avatar." + avatarType);
|
|
||||||
if (!oldAvatar.remove()) {
|
|
||||||
qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing";
|
|
||||||
} else {
|
|
||||||
avatarType = "";
|
|
||||||
avatarHash = "";
|
|
||||||
avaChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avaChanged) {
|
|
||||||
QMap<QString, QVariant> change;
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
|
||||||
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
|
||||||
change.insert("avatarPath", path + "avatar." + avatarType);
|
|
||||||
} else {
|
|
||||||
acc->presence.setPhotoHash("");
|
|
||||||
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
|
|
||||||
change.insert("avatarPath", "");
|
|
||||||
}
|
|
||||||
acc->client.setClientPresence(acc->presence);
|
|
||||||
emit acc->changed(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
ownVCardRequestInProgress = false;
|
|
||||||
|
|
||||||
Shared::VCard vCard;
|
|
||||||
initializeVCard(vCard, card);
|
|
||||||
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::valid);
|
|
||||||
vCard.setAvatarPath(path + "avatar." + avatarType);
|
|
||||||
} else {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit acc->receivedVCard(acc->getBareJid(), vCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::VCardHandler::handleOffline()
|
|
||||||
{
|
|
||||||
pendingVCardRequests.clear();
|
|
||||||
Shared::VCard vCard; //just to show, that there is now more pending request
|
|
||||||
for (const QString& jid : pendingVCardRequests) {
|
|
||||||
emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error
|
|
||||||
}
|
|
||||||
pendingVCardRequests.clear();
|
|
||||||
ownVCardRequestInProgress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::VCardHandler::requestVCard(const QString& jid)
|
|
||||||
{
|
|
||||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
|
||||||
qDebug() << "requesting vCard" << jid;
|
|
||||||
if (jid == acc->getBareJid()) {
|
|
||||||
if (!ownVCardRequestInProgress) {
|
|
||||||
acc->vm->requestClientVCard();
|
|
||||||
ownVCardRequestInProgress = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
acc->vm->requestVCard(jid);
|
|
||||||
pendingVCardRequests.insert(jid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence)
|
|
||||||
{
|
|
||||||
if (!ownVCardRequestInProgress) {
|
|
||||||
switch (p_presence.vCardUpdateType()) {
|
|
||||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
acc->vm->requestClientVCard();
|
|
||||||
ownVCardRequestInProgress = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
|
||||||
if (avatarHash != p_presence.photoHash()) {
|
|
||||||
acc->vm->requestClientVCard();
|
|
||||||
ownVCardRequestInProgress = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::VCardHandler::uploadVCard(const Shared::VCard& card)
|
|
||||||
{
|
|
||||||
QXmppVCardIq iq;
|
|
||||||
initializeQXmppVCard(iq, card);
|
|
||||||
|
|
||||||
if (card.getAvatarType() != Shared::Avatar::empty) {
|
|
||||||
QString newPath = card.getAvatarPath();
|
|
||||||
QString oldPath = getAvatarPath();
|
|
||||||
QByteArray data;
|
|
||||||
QString type;
|
|
||||||
if (newPath != oldPath) {
|
|
||||||
QFile avatar(newPath);
|
|
||||||
if (!avatar.open(QFile::ReadOnly)) {
|
|
||||||
qDebug() << "An attempt to upload new vCard to account" << acc->name
|
|
||||||
<< "but it wasn't possible to read file" << newPath
|
|
||||||
<< "which was supposed to be new avatar, uploading old avatar";
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
QFile oA(oldPath);
|
|
||||||
if (!oA.open(QFile::ReadOnly)) {
|
|
||||||
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
|
|
||||||
} else {
|
|
||||||
data = oA.readAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data = avatar.readAll();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
QFile oA(oldPath);
|
|
||||||
if (!oA.open(QFile::ReadOnly)) {
|
|
||||||
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
|
|
||||||
} else {
|
|
||||||
data = oA.readAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.size() > 0) {
|
|
||||||
QMimeDatabase db;
|
|
||||||
type = db.mimeTypeForData(data).name();
|
|
||||||
iq.setPhoto(data);
|
|
||||||
iq.setPhotoType(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
acc->vm->setClientVCard(iq);
|
|
||||||
onOwnVCardReceived(iq);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::VCardHandler::getAvatarPath() const
|
|
||||||
{
|
|
||||||
if (avatarType.size() == 0) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
// Squawk messenger.
|
|
||||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#ifndef CORE_VCARDHANDLER_H
|
|
||||||
#define CORE_VCARDHANDLER_H
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <QXmppVCardIq.h>
|
|
||||||
#include <QXmppPresence.h>
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include <shared/vcard.h>
|
|
||||||
#include <core/adapterfunctions.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
|
|
||||||
class Account;
|
|
||||||
|
|
||||||
class VCardHandler : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
VCardHandler(Account* account);
|
|
||||||
~VCardHandler();
|
|
||||||
|
|
||||||
void handleOffline();
|
|
||||||
void requestVCard(const QString& jid);
|
|
||||||
void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence);
|
|
||||||
void uploadVCard(const Shared::VCard& card);
|
|
||||||
QString getAvatarPath() const;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onVCardReceived(const QXmppVCardIq& card);
|
|
||||||
void onOwnVCardReceived(const QXmppVCardIq& card);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Account* acc;
|
|
||||||
|
|
||||||
bool ownVCardRequestInProgress;
|
|
||||||
std::set<QString> pendingVCardRequests;
|
|
||||||
QString avatarHash;
|
|
||||||
QString avatarType;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CORE_VCARDHANDLER_H
|
|
@ -28,11 +28,8 @@ Core::NetworkAccess::NetworkAccess(QObject* parent):
|
|||||||
manager(0),
|
manager(0),
|
||||||
storage("fileURLStorage"),
|
storage("fileURLStorage"),
|
||||||
downloads(),
|
downloads(),
|
||||||
uploads(),
|
uploads()
|
||||||
currentPath()
|
|
||||||
{
|
{
|
||||||
QSettings settings;
|
|
||||||
currentPath = settings.value("downloadsPath").toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::NetworkAccess::~NetworkAccess()
|
Core::NetworkAccess::~NetworkAccess()
|
||||||
@ -73,9 +70,6 @@ void Core::NetworkAccess::start()
|
|||||||
{
|
{
|
||||||
if (!running) {
|
if (!running) {
|
||||||
manager = new QNetworkAccessManager();
|
manager = new QNetworkAccessManager();
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
||||||
manager->setTransferTimeout();
|
|
||||||
#endif
|
|
||||||
storage.open();
|
storage.open();
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
@ -105,56 +99,31 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||||||
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
if (dwn->success) {
|
qreal received = bytesReceived;
|
||||||
qreal received = bytesReceived;
|
qreal total = bytesTotal;
|
||||||
qreal total = bytesTotal;
|
qreal progress = received/total;
|
||||||
qreal progress = received/total;
|
dwn->progress = progress;
|
||||||
dwn->progress = progress;
|
emit loadFileProgress(dwn->messages, progress, false);
|
||||||
emit loadFileProgress(dwn->messages, progress, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
||||||
{
|
{
|
||||||
qDebug() << "DEBUG: DOWNLOAD ERROR";
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
qDebug() << rpl->errorString();
|
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
QString errorText = getErrorText(code);
|
QString errorText = getErrorText(code);
|
||||||
//if (errorText.size() > 0) {
|
if (errorText.size() > 0) {
|
||||||
itr->second->success = false;
|
itr->second->success = false;
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
emit loadFileError(dwn->messages, errorText, false);
|
emit loadFileError(dwn->messages, errorText, false);
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors)
|
|
||||||
{
|
|
||||||
qDebug() << "DEBUG: DOWNLOAD SSL ERRORS";
|
|
||||||
for (const QSslError& err : errors) {
|
|
||||||
qDebug() << err.errorString();
|
|
||||||
}
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
|
||||||
QString url = rpl->url().toString();
|
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
|
||||||
if (itr == downloads.end()) {
|
|
||||||
qDebug() << "an SSL error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
|
|
||||||
} else {
|
|
||||||
//if (errorText.size() > 0) {
|
|
||||||
itr->second->success = false;
|
|
||||||
Transfer* dwn = itr->second;
|
|
||||||
emit loadFileError(dwn->messages, "SSL errors occured", false);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
||||||
{
|
{
|
||||||
QString errorText("");
|
QString errorText("");
|
||||||
@ -177,11 +146,7 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
errorText = "Connection was closed because it timed out";
|
errorText = "Connection was closed because it timed out";
|
||||||
break;
|
break;
|
||||||
case QNetworkReply::OperationCanceledError:
|
case QNetworkReply::OperationCanceledError:
|
||||||
//this means I closed it myself by abort() or close()
|
//this means I closed it myself by abort() or close(), don't think I need to notify here
|
||||||
//I don't call them directory, but this is the error code
|
|
||||||
//Qt returns when it can not resume donwload after the network failure
|
|
||||||
//or when the download is canceled by the timout;
|
|
||||||
errorText = "Connection lost";
|
|
||||||
break;
|
break;
|
||||||
case QNetworkReply::SslHandshakeFailedError:
|
case QNetworkReply::SslHandshakeFailedError:
|
||||||
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
|
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
|
||||||
@ -282,7 +247,6 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
|
|
||||||
void Core::NetworkAccess::onDownloadFinished()
|
void Core::NetworkAccess::onDownloadFinished()
|
||||||
{
|
{
|
||||||
qDebug() << "DEBUG: DOWNLOAD FINISHED";
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
@ -292,20 +256,17 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
if (dwn->success) {
|
if (dwn->success) {
|
||||||
qDebug() << "download success for" << url;
|
qDebug() << "download success for" << url;
|
||||||
QString err;
|
|
||||||
QStringList hops = url.split("/");
|
QStringList hops = url.split("/");
|
||||||
QString fileName = hops.back();
|
QString fileName = hops.back();
|
||||||
QString jid;
|
QString jid;
|
||||||
if (dwn->messages.size() > 0) {
|
if (dwn->messages.size() > 0) {
|
||||||
jid = dwn->messages.front().jid;
|
jid = dwn->messages.front().jid;
|
||||||
} else {
|
|
||||||
qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken";
|
|
||||||
}
|
}
|
||||||
QString path = prepareDirectory(jid);
|
QString path = prepareDirectory(jid);
|
||||||
if (path.size() > 0) {
|
if (path.size() > 0) {
|
||||||
path = checkFileName(fileName, path);
|
path = checkFileName(fileName, path);
|
||||||
|
|
||||||
QFile file(Shared::resolvePath(path));
|
QFile file(path);
|
||||||
if (file.open(QIODevice::WriteOnly)) {
|
if (file.open(QIODevice::WriteOnly)) {
|
||||||
file.write(dwn->reply->readAll());
|
file.write(dwn->reply->readAll());
|
||||||
file.close();
|
file.close();
|
||||||
@ -313,16 +274,15 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
qDebug() << "file" << path << "was successfully downloaded";
|
qDebug() << "file" << path << "was successfully downloaded";
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "couldn't save file" << path;
|
qDebug() << "couldn't save file" << path;
|
||||||
err = "Error opening file to write:" + file.errorString();
|
path = QString();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = "Couldn't prepare a directory for file";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.size() > 0) {
|
if (path.size() > 0) {
|
||||||
emit downloadFileComplete(dwn->messages, path);
|
emit downloadFileComplete(dwn->messages, path);
|
||||||
} else {
|
} else {
|
||||||
emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false);
|
//TODO do I need to handle the failure here or it's already being handled in error?
|
||||||
|
//emit loadFileError(dwn->messages, path, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +298,6 @@ void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& ms
|
|||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
dwn->reply = manager->get(req);
|
dwn->reply = manager->get(req);
|
||||||
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
||||||
connect(dwn->reply, &QNetworkReply::sslErrors, this, &NetworkAccess::onDownloadSSLError);
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onDownloadError);
|
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onDownloadError);
|
||||||
#else
|
#else
|
||||||
@ -358,11 +317,11 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
|||||||
qDebug() << "an error uploading" << url << ": the request is reporting an error but there is no record of it being uploading, ignoring";
|
qDebug() << "an error uploading" << url << ": the request is reporting an error but there is no record of it being uploading, ignoring";
|
||||||
} else {
|
} else {
|
||||||
QString errorText = getErrorText(code);
|
QString errorText = getErrorText(code);
|
||||||
//if (errorText.size() > 0) {
|
if (errorText.size() > 0) {
|
||||||
itr->second->success = false;
|
itr->second->success = false;
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
emit loadFileError(upl->messages, errorText, true);
|
emit loadFileError(upl->messages, errorText, true);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//TODO deletion?
|
//TODO deletion?
|
||||||
}
|
}
|
||||||
@ -379,34 +338,9 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
if (upl->success) {
|
if (upl->success) {
|
||||||
qDebug() << "upload success for" << url;
|
qDebug() << "upload success for" << url;
|
||||||
|
|
||||||
// Copy file to Download folder if it is a temp file. See Conversation::onImagePasted.
|
|
||||||
if (upl->path.startsWith(QDir::tempPath() + QDir::separator() + QStringLiteral("squawk_img_attach_")) && upl->path.endsWith(".png")) {
|
|
||||||
QString err = "";
|
|
||||||
QString downloadDirPath = prepareDirectory(upl->messages.front().jid);
|
|
||||||
if (downloadDirPath.size() > 0) {
|
|
||||||
QString newPath = downloadDirPath + QDir::separator() + upl->path.mid(QDir::tempPath().length() + 1);
|
|
||||||
|
|
||||||
// Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder
|
|
||||||
bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath));
|
|
||||||
|
|
||||||
if (copyResult) {
|
|
||||||
// Change storage
|
|
||||||
upl->path = newPath;
|
|
||||||
} else {
|
|
||||||
err = "copying to " + newPath + " failed";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = "Couldn't prepare a directory for file";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err.size() != 0) {
|
|
||||||
qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.addFile(upl->messages, upl->url, upl->path);
|
storage.addFile(upl->messages, upl->url, upl->path);
|
||||||
emit uploadFileComplete(upl->messages, upl->url, upl->path);
|
emit uploadFileComplete(upl->messages, upl->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
upl->reply->deleteLater();
|
upl->reply->deleteLater();
|
||||||
@ -426,24 +360,22 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
|
|||||||
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
if (upl->success) {
|
qreal received = bytesReceived;
|
||||||
qreal received = bytesReceived;
|
qreal total = bytesTotal;
|
||||||
qreal total = bytesTotal;
|
qreal progress = received/total;
|
||||||
qreal progress = received/total;
|
upl->progress = progress;
|
||||||
upl->progress = progress;
|
emit loadFileProgress(upl->messages, progress, true);
|
||||||
emit loadFileProgress(upl->messages, progress, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
||||||
{
|
{
|
||||||
QString p = Shared::squawkifyPath(path);
|
QString p;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
p = storage.getUrl(p);
|
p = storage.getUrl(path);
|
||||||
} catch (const Archive::NotFound& err) {
|
} catch (const Archive::NotFound& err) {
|
||||||
p = "";
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@ -518,13 +450,11 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri
|
|||||||
|
|
||||||
QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
||||||
{
|
{
|
||||||
QString path = currentPath;
|
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||||
QString addition;
|
path += "/" + QApplication::applicationName();
|
||||||
if (jid.size() > 0) {
|
if (jid.size() > 0) {
|
||||||
addition = jid;
|
path += "/" + jid;
|
||||||
path += QDir::separator() + jid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir location(path);
|
QDir location(path);
|
||||||
|
|
||||||
if (!location.exists()) {
|
if (!location.exists()) {
|
||||||
@ -532,10 +462,10 @@ QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
|||||||
if (!res) {
|
if (!res) {
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
return "squawk://" + addition;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "squawk://" + addition;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
|
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
|
||||||
@ -549,17 +479,14 @@ QString Core::NetworkAccess::checkFileName(const QString& name, const QString& p
|
|||||||
suffix += "." + (*sItr);
|
suffix += "." + (*sItr);
|
||||||
}
|
}
|
||||||
QString postfix("");
|
QString postfix("");
|
||||||
QString resolvedPath = Shared::resolvePath(path);
|
QFileInfo proposedName(path + "/" + realName + suffix);
|
||||||
QString count("");
|
|
||||||
QFileInfo proposedName(resolvedPath + QDir::separator() + realName + count + suffix);
|
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
while (proposedName.exists()) {
|
while (proposedName.exists()) {
|
||||||
count = QString("(") + std::to_string(++counter).c_str() + ")";
|
QString count = QString("(") + std::to_string(++counter).c_str() + ")";
|
||||||
proposedName = QFileInfo(resolvedPath + QDir::separator() + realName + count + suffix);
|
proposedName = QFileInfo(path + "/" + realName + count + suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path + QDir::separator() + realName + count + suffix;
|
return proposedName.absoluteFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
@ -571,19 +498,3 @@ std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QStr
|
|||||||
{
|
{
|
||||||
return storage.deletedFile(path);
|
return storage.deletedFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::moveFilesDirectory(const QString& newPath)
|
|
||||||
{
|
|
||||||
QDir dir(currentPath);
|
|
||||||
bool success = true;
|
|
||||||
qDebug() << "moving" << currentPath << "to" << newPath;
|
|
||||||
for (QFileInfo fileInfo : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) {
|
|
||||||
QString fileName = fileInfo.fileName();
|
|
||||||
success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
qDebug() << "couldn't move downloads directory, most probably downloads will be broken";
|
|
||||||
}
|
|
||||||
currentPath = newPath;
|
|
||||||
}
|
|
||||||
|
@ -26,12 +26,10 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QSettings>
|
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "storage/urlstorage.h"
|
#include "urlstorage.h"
|
||||||
#include "shared/pathcheck.h"
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -60,14 +58,13 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
||||||
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path);
|
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
||||||
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void downladFile(const QString& url);
|
void downladFile(const QString& url);
|
||||||
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
||||||
void moveFilesDirectory(const QString& newPath);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
||||||
@ -78,7 +75,6 @@ private:
|
|||||||
private slots:
|
private slots:
|
||||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
void onDownloadError(QNetworkReply::NetworkError code);
|
void onDownloadError(QNetworkReply::NetworkError code);
|
||||||
void onDownloadSSLError(const QList<QSslError> &errors);
|
|
||||||
void onDownloadFinished();
|
void onDownloadFinished();
|
||||||
void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
void onUploadError(QNetworkReply::NetworkError code);
|
void onUploadError(QNetworkReply::NetworkError code);
|
||||||
@ -90,7 +86,6 @@ private:
|
|||||||
UrlStorage storage;
|
UrlStorage storage;
|
||||||
std::map<QString, Transfer*> downloads;
|
std::map<QString, Transfer*> downloads;
|
||||||
std::map<QString, Transfer*> uploads;
|
std::map<QString, Transfer*> uploads;
|
||||||
QString currentPath;
|
|
||||||
|
|
||||||
struct Transfer {
|
struct Transfer {
|
||||||
std::list<Shared::MessageInfo> messages;
|
std::list<Shared::MessageInfo> messages;
|
||||||
|
@ -1,9 +1,37 @@
|
|||||||
if (WITH_KWALLET)
|
cmake_minimum_required(VERSION 3.3)
|
||||||
target_sources(squawk PRIVATE
|
project(pse)
|
||||||
kwallet.cpp
|
|
||||||
kwallet.h
|
if (WITH_KWALLET)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
|
find_package(Qt5Core CONFIG REQUIRED)
|
||||||
|
find_package(Qt5Gui CONFIG REQUIRED)
|
||||||
|
|
||||||
|
get_target_property(KWALLET_INTERFACE_INCLUDE_DIRECTORIES KF5::Wallet INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
get_target_property(Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES Qt5::Gui INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
|
||||||
|
set(kwalletPSE_SRC
|
||||||
|
kwallet.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(kwalletPSE STATIC ${kwalletPSE_SRC})
|
||||||
|
|
||||||
|
target_include_directories(kwalletPSE PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
target_include_directories(kwalletPSE PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
|
||||||
|
target_link_libraries(kwalletPSE Qt5::Core)
|
||||||
|
|
||||||
|
set(kwalletW_SRC
|
||||||
|
wrappers/kwallet.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(wrappers)
|
add_library(kwalletWrapper SHARED ${kwalletW_SRC})
|
||||||
target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>)
|
|
||||||
endif ()
|
target_include_directories(kwalletWrapper PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
target_include_directories(kwalletWrapper PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
|
||||||
|
target_link_libraries(kwalletWrapper KF5::Wallet)
|
||||||
|
target_link_libraries(kwalletWrapper Qt5::Core)
|
||||||
|
|
||||||
|
install(TARGETS kwalletWrapper DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
|
endif()
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
add_library(kwalletWrapper SHARED kwallet.cpp)
|
|
||||||
target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet)
|
|
||||||
|
|
||||||
install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
@ -1,21 +1,3 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <KF5/KWallet/KWallet>
|
#include <KF5/KWallet/KWallet>
|
||||||
|
|
||||||
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
|
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
|
||||||
|
@ -124,19 +124,15 @@ void Core::RosterItem::nextRequest()
|
|||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
bool last = false;
|
bool last = false;
|
||||||
if (archiveState == beginning || archiveState == complete) {
|
if (archiveState == beginning || archiveState == complete) {
|
||||||
try {
|
QString firstId = archive->oldestId();
|
||||||
QString firstId = archive->oldestId();
|
if (responseCache.size() == 0) {
|
||||||
if (responseCache.size() == 0) {
|
if (requestedBefore == firstId) {
|
||||||
if (requestedBefore == firstId) {
|
last = true;
|
||||||
last = true;
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if (responseCache.front().getId() == firstId) {
|
||||||
if (responseCache.front().getId() == firstId) {
|
last = true;
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (const Archive::Empty& e) {
|
|
||||||
last = true;
|
|
||||||
}
|
}
|
||||||
} else if (archiveState == empty && responseCache.size() == 0) {
|
} else if (archiveState == empty && responseCache.size() == 0) {
|
||||||
last = true;
|
last = true;
|
||||||
@ -175,12 +171,8 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
}
|
}
|
||||||
try {
|
Shared::Message msg = archive->newest();
|
||||||
Shared::Message msg = archive->newest();
|
emit needHistory("", getId(msg), msg.getTime());
|
||||||
emit needHistory("", getId(msg), msg.getTime());
|
|
||||||
} catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example)
|
|
||||||
emit needHistory(before, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case end:
|
case end:
|
||||||
|
@ -34,8 +34,7 @@
|
|||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/vcard.h"
|
#include "shared/vcard.h"
|
||||||
#include "storage/archive.h"
|
#include "archive.h"
|
||||||
#include "adapterfunctions.h"
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2021 Shunf4 <shun1048576@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "signalcatcher.h"
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
|
||||||
QObject(parent),
|
|
||||||
app(p_app)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SignalCatcher::~SignalCatcher()
|
|
||||||
{}
|
|
||||||
|
|
||||||
void SignalCatcher::handleSigInt()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SignalCatcher::intSignalHandler(int unused)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int SignalCatcher::setup_unix_signal_handlers()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
377
core/squawk.cpp
377
core/squawk.cpp
@ -26,9 +26,8 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
QObject(parent),
|
QObject(parent),
|
||||||
accounts(),
|
accounts(),
|
||||||
amap(),
|
amap(),
|
||||||
state(Shared::Availability::offline),
|
|
||||||
network(),
|
network(),
|
||||||
isInitialized(false)
|
waitingForAccounts(0)
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
,kwallet()
|
,kwallet()
|
||||||
#endif
|
#endif
|
||||||
@ -42,7 +41,7 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
||||||
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
||||||
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::responsePassword);
|
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword);
|
||||||
|
|
||||||
Shared::Global::setSupported("KWallet", true);
|
Shared::Global::setSupported("KWallet", true);
|
||||||
}
|
}
|
||||||
@ -67,43 +66,39 @@ void Core::Squawk::stop()
|
|||||||
{
|
{
|
||||||
qDebug("Stopping squawk core..");
|
qDebug("Stopping squawk core..");
|
||||||
network.stop();
|
network.stop();
|
||||||
|
QSettings settings;
|
||||||
if (isInitialized) {
|
settings.beginGroup("core");
|
||||||
QSettings settings;
|
settings.beginWriteArray("accounts");
|
||||||
settings.beginGroup("core");
|
SimpleCrypt crypto(passwordHash);
|
||||||
settings.beginWriteArray("accounts");
|
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
|
||||||
SimpleCrypt crypto(passwordHash);
|
settings.setArrayIndex(i);
|
||||||
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
|
Account* acc = accounts[i];
|
||||||
settings.setArrayIndex(i);
|
|
||||||
Account* acc = accounts[i];
|
Shared::AccountPassword ap = acc->getPasswordType();
|
||||||
|
QString password;
|
||||||
Shared::AccountPassword ap = acc->getPasswordType();
|
|
||||||
QString password;
|
switch (ap) {
|
||||||
|
case Shared::AccountPassword::plain:
|
||||||
switch (ap) {
|
password = acc->getPassword();
|
||||||
case Shared::AccountPassword::plain:
|
break;
|
||||||
password = acc->getPassword();
|
case Shared::AccountPassword::jammed:
|
||||||
break;
|
password = crypto.encryptToString(acc->getPassword());
|
||||||
case Shared::AccountPassword::jammed:
|
break;
|
||||||
password = crypto.encryptToString(acc->getPassword());
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.setValue("name", acc->getName());
|
|
||||||
settings.setValue("server", acc->getServer());
|
|
||||||
settings.setValue("login", acc->getLogin());
|
|
||||||
settings.setValue("password", password);
|
|
||||||
settings.setValue("resource", acc->getResource());
|
|
||||||
settings.setValue("passwordType", static_cast<int>(ap));
|
|
||||||
settings.setValue("active", acc->getActive());
|
|
||||||
}
|
}
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
settings.setValue("name", acc->getName());
|
||||||
|
settings.setValue("server", acc->getServer());
|
||||||
settings.sync();
|
settings.setValue("login", acc->getLogin());
|
||||||
|
settings.setValue("password", password);
|
||||||
|
settings.setValue("resource", acc->getResource());
|
||||||
|
settings.setValue("passwordType", static_cast<int>(ap));
|
||||||
}
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
settings.sync();
|
||||||
|
|
||||||
emit quit();
|
emit quit();
|
||||||
}
|
}
|
||||||
@ -113,7 +108,6 @@ void Core::Squawk::start()
|
|||||||
qDebug("Starting squawk core..");
|
qDebug("Starting squawk core..");
|
||||||
|
|
||||||
readSettings();
|
readSettings();
|
||||||
isInitialized = true;
|
|
||||||
network.start();
|
network.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +118,8 @@ void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
|
|||||||
QString server = map.value("server").toString();
|
QString server = map.value("server").toString();
|
||||||
QString password = map.value("password").toString();
|
QString password = map.value("password").toString();
|
||||||
QString resource = map.value("resource").toString();
|
QString resource = map.value("resource").toString();
|
||||||
int passwordType = map.value("passwordType").toInt();
|
|
||||||
bool active = map.value("active").toBool();
|
|
||||||
|
|
||||||
addAccount(login, server, password, name, resource, active, Shared::Global::fromInt<Shared::AccountPassword>(passwordType));
|
addAccount(login, server, password, name, resource, Shared::AccountPassword::plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addAccount(
|
void Core::Squawk::addAccount(
|
||||||
@ -135,15 +127,13 @@ void Core::Squawk::addAccount(
|
|||||||
const QString& server,
|
const QString& server,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
bool active,
|
Shared::AccountPassword passwordType
|
||||||
Shared::AccountPassword passwordType)
|
)
|
||||||
{
|
{
|
||||||
if (amap.count(name) > 0) {
|
QSettings settings;
|
||||||
qDebug() << "An attempt to add account" << name << "but an account with such name already exist, ignoring";
|
|
||||||
return;
|
Account* acc = new Account(login, server, password, name, &network);
|
||||||
}
|
|
||||||
Account* acc = new Account(login, server, password, name, active, &network);
|
|
||||||
acc->setResource(resource);
|
acc->setResource(resource);
|
||||||
acc->setPasswordType(passwordType);
|
acc->setPasswordType(passwordType);
|
||||||
accounts.push_back(acc);
|
accounts.push_back(acc);
|
||||||
@ -152,8 +142,6 @@ void Core::Squawk::addAccount(
|
|||||||
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
||||||
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
||||||
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
||||||
connect(acc, &Account::needPassword, this, &Squawk::onAccountNeedPassword);
|
|
||||||
|
|
||||||
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
||||||
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
||||||
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
||||||
@ -191,44 +179,20 @@ void Core::Squawk::addAccount(
|
|||||||
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
||||||
{"error", ""},
|
{"error", ""},
|
||||||
{"avatarPath", acc->getAvatarPath()},
|
{"avatarPath", acc->getAvatarPath()},
|
||||||
{"passwordType", QVariant::fromValue(passwordType)},
|
{"passwordType", QVariant::fromValue(passwordType)}
|
||||||
{"active", active}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
emit newAccount(map);
|
emit newAccount(map);
|
||||||
|
|
||||||
switch (passwordType) {
|
|
||||||
case Shared::AccountPassword::alwaysAsk:
|
|
||||||
case Shared::AccountPassword::kwallet:
|
|
||||||
if (password == "") {
|
|
||||||
acc->invalidatePassword();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state != Shared::Availability::offline) {
|
|
||||||
acc->setAvailability(state);
|
|
||||||
if (acc->getActive()) {
|
|
||||||
acc->connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::changeState(Shared::Availability p_state)
|
void Core::Squawk::changeState(Shared::Availability p_state)
|
||||||
{
|
{
|
||||||
if (state != p_state) {
|
if (state != p_state) {
|
||||||
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
|
||||||
Account* acc = *itr;
|
|
||||||
acc->setAvailability(p_state);
|
|
||||||
if (state == Shared::Availability::offline && acc->getActive()) {
|
|
||||||
acc->connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state = p_state;
|
state = p_state;
|
||||||
|
}
|
||||||
emit stateChanged(p_state);
|
|
||||||
|
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
||||||
|
(*itr)->setAvailability(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,10 +203,7 @@ void Core::Squawk::connectAccount(const QString& account)
|
|||||||
qDebug("An attempt to connect non existing account, skipping");
|
qDebug("An attempt to connect non existing account, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->setActive(true);
|
itr->second->connect();
|
||||||
if (state != Shared::Availability::offline) {
|
|
||||||
itr->second->connect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::disconnectAccount(const QString& account)
|
void Core::Squawk::disconnectAccount(const QString& account)
|
||||||
@ -253,18 +214,14 @@ void Core::Squawk::disconnectAccount(const QString& account)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
itr->second->setActive(false);
|
|
||||||
itr->second->disconnect();
|
itr->second->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state)
|
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state)
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {
|
emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}});
|
||||||
{"state", QVariant::fromValue(p_state)},
|
|
||||||
{"error", ""}
|
|
||||||
});
|
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
if (p_state == Shared::ConnectionState::connected) {
|
if (p_state == Shared::ConnectionState::connected) {
|
||||||
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
|
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
|
||||||
@ -272,6 +229,33 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Accounts::const_iterator itr = accounts.begin();
|
||||||
|
bool es = true;
|
||||||
|
bool ea = true;
|
||||||
|
Shared::ConnectionState cs = (*itr)->getState();
|
||||||
|
Shared::Availability av = (*itr)->getAvailability();
|
||||||
|
itr++;
|
||||||
|
for (Accounts::const_iterator end = accounts.end(); itr != end; itr++) {
|
||||||
|
Account* item = *itr;
|
||||||
|
if (item->getState() != cs) {
|
||||||
|
es = false;
|
||||||
|
}
|
||||||
|
if (item->getAvailability() != av) {
|
||||||
|
ea = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (es) {
|
||||||
|
if (cs == Shared::ConnectionState::disconnected) {
|
||||||
|
state = Shared::Availability::offline;
|
||||||
|
emit stateChanged(state);
|
||||||
|
} else if (ea) {
|
||||||
|
state = av;
|
||||||
|
emit stateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
@ -344,35 +328,13 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
|
|||||||
{
|
{
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to send a message with non existing account" << account << ", skipping";
|
qDebug("An attempt to send a message with non existing account, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
itr->second->sendMessage(data);
|
itr->second->sendMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data)
|
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
|
||||||
if (itr == amap.end()) {
|
|
||||||
qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
itr->second->replaceMessage(originalId, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
|
||||||
if (itr == amap.end()) {
|
|
||||||
qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
itr->second->resendMessage(jid, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before)
|
void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before)
|
||||||
{
|
{
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
@ -401,7 +363,6 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
Shared::ConnectionState st = acc->getState();
|
Shared::ConnectionState st = acc->getState();
|
||||||
QMap<QString, QVariant>::const_iterator mItr;
|
QMap<QString, QVariant>::const_iterator mItr;
|
||||||
bool needToReconnect = false;
|
bool needToReconnect = false;
|
||||||
bool wentReconnecting = false;
|
|
||||||
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end()) {
|
||||||
@ -427,16 +388,8 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool activeChanged = false;
|
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
||||||
mItr = map.find("active");
|
acc->reconnect();
|
||||||
if (mItr == map.end() || mItr->toBool() == acc->getActive()) {
|
|
||||||
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
|
||||||
acc->reconnect();
|
|
||||||
wentReconnecting = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
acc->setActive(mItr->toBool());
|
|
||||||
activeChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
@ -473,14 +426,6 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (state != Shared::Availability::offline) {
|
|
||||||
if (activeChanged && acc->getActive()) {
|
|
||||||
acc->connect();
|
|
||||||
} else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) {
|
|
||||||
acc->connect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit changeAccount(name, map);
|
emit changeAccount(name, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,10 +433,6 @@ void Core::Squawk::onAccountError(const QString& text)
|
|||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {{"error", text}});
|
emit changeAccount(acc->getName(), {{"error", text}});
|
||||||
|
|
||||||
if (acc->getLastError() == Account::Error::authentication) {
|
|
||||||
emit requestPassword(acc->getName(), true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::removeAccountRequest(const QString& name)
|
void Core::Squawk::removeAccountRequest(const QString& name)
|
||||||
@ -706,70 +647,6 @@ void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card
|
|||||||
itr->second->uploadVCard(card);
|
itr->second->uploadVCard(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::readSettings()
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
settings.beginGroup("core");
|
|
||||||
int size = settings.beginReadArray("accounts");
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
settings.setArrayIndex(i);
|
|
||||||
Shared::AccountPassword passwordType =
|
|
||||||
Shared::Global::fromInt<Shared::AccountPassword>(
|
|
||||||
settings.value("passwordType", static_cast<int>(Shared::AccountPassword::plain)).toInt()
|
|
||||||
);
|
|
||||||
|
|
||||||
QString password = settings.value("password", "").toString();
|
|
||||||
if (passwordType == Shared::AccountPassword::jammed) {
|
|
||||||
SimpleCrypt crypto(passwordHash);
|
|
||||||
password = crypto.decryptToString(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
addAccount(
|
|
||||||
settings.value("login").toString(),
|
|
||||||
settings.value("server").toString(),
|
|
||||||
password,
|
|
||||||
settings.value("name").toString(),
|
|
||||||
settings.value("resource").toString(),
|
|
||||||
settings.value("active").toBool(),
|
|
||||||
passwordType
|
|
||||||
);
|
|
||||||
}
|
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
|
||||||
|
|
||||||
qDebug() << "Squawk core is ready";
|
|
||||||
emit ready();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onAccountNeedPassword()
|
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
|
||||||
switch (acc->getPasswordType()) {
|
|
||||||
case Shared::AccountPassword::alwaysAsk:
|
|
||||||
emit requestPassword(acc->getName(), false);
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::kwallet: {
|
|
||||||
#ifdef WITH_KWALLET
|
|
||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
|
||||||
kwallet.requestReadPassword(acc->getName());
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
emit requestPassword(acc->getName(), false);
|
|
||||||
#ifdef WITH_KWALLET
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break; //should never happen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onWalletRejectPassword(const QString& login)
|
|
||||||
{
|
|
||||||
emit requestPassword(login, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::responsePassword(const QString& account, const QString& password)
|
void Core::Squawk::responsePassword(const QString& account, const QString& password)
|
||||||
{
|
{
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
@ -777,12 +654,96 @@ void Core::Squawk::responsePassword(const QString& account, const QString& passw
|
|||||||
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Account* acc = itr->second;
|
itr->second->setPassword(password);
|
||||||
acc->setPassword(password);
|
|
||||||
emit changeAccount(account, {{"password", password}});
|
emit changeAccount(account, {{"password", password}});
|
||||||
if (state != Shared::Availability::offline && acc->getActive()) {
|
accountReady();
|
||||||
acc->connect();
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::readSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("core");
|
||||||
|
int size = settings.beginReadArray("accounts");
|
||||||
|
waitingForAccounts = size;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
parseAccount(
|
||||||
|
settings.value("login").toString(),
|
||||||
|
settings.value("server").toString(),
|
||||||
|
settings.value("password", "").toString(),
|
||||||
|
settings.value("name").toString(),
|
||||||
|
settings.value("resource").toString(),
|
||||||
|
Shared::Global::fromInt<Shared::AccountPassword>(settings.value("passwordType", static_cast<int>(Shared::AccountPassword::plain)).toInt())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::accountReady()
|
||||||
|
{
|
||||||
|
--waitingForAccounts;
|
||||||
|
|
||||||
|
if (waitingForAccounts == 0) {
|
||||||
|
emit ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::parseAccount(
|
||||||
|
const QString& login,
|
||||||
|
const QString& server,
|
||||||
|
const QString& password,
|
||||||
|
const QString& name,
|
||||||
|
const QString& resource,
|
||||||
|
Shared::AccountPassword passwordType
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (passwordType) {
|
||||||
|
case Shared::AccountPassword::plain:
|
||||||
|
addAccount(login, server, password, name, resource, passwordType);
|
||||||
|
accountReady();
|
||||||
|
break;
|
||||||
|
case Shared::AccountPassword::jammed: {
|
||||||
|
SimpleCrypt crypto(passwordHash);
|
||||||
|
QString decrypted = crypto.decryptToString(password);
|
||||||
|
addAccount(login, server, decrypted, name, resource, passwordType);
|
||||||
|
accountReady();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Shared::AccountPassword::alwaysAsk:
|
||||||
|
addAccount(login, server, QString(), name, resource, passwordType);
|
||||||
|
emit requestPassword(name);
|
||||||
|
break;
|
||||||
|
case Shared::AccountPassword::kwallet: {
|
||||||
|
addAccount(login, server, QString(), name, resource, passwordType);
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
|
kwallet.requestReadPassword(name);
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
emit requestPassword(name);
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onWalletRejectPassword(const QString& login)
|
||||||
|
{
|
||||||
|
emit requestPassword(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onWalletResponsePassword(const QString& login, const QString& password)
|
||||||
|
{
|
||||||
|
AccountsMap::const_iterator itr = amap.find(login);
|
||||||
|
if (itr == amap.end()) {
|
||||||
|
qDebug() << "An attempt to set password to non existing account" << login << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itr->second->setPassword(password);
|
||||||
|
emit changeAccount(login, {{"password", password}});
|
||||||
|
accountReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
||||||
@ -807,9 +768,3 @@ void Core::Squawk::onLocalPathInvalid(const QString& path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::changeDownloadsPath(const QString& path)
|
|
||||||
{
|
|
||||||
network.moveFilesDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ signals:
|
|||||||
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void requestPassword(const QString& account, bool authernticationError);
|
void requestPassword(const QString& account);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
@ -101,8 +101,6 @@ public slots:
|
|||||||
void changeState(Shared::Availability state);
|
void changeState(Shared::Availability state);
|
||||||
|
|
||||||
void sendMessage(const QString& account, const Shared::Message& data);
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
|
||||||
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
|
||||||
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
||||||
|
|
||||||
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
@ -124,7 +122,6 @@ public slots:
|
|||||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||||
void responsePassword(const QString& account, const QString& password);
|
void responsePassword(const QString& account, const QString& password);
|
||||||
void onLocalPathInvalid(const QString& path);
|
void onLocalPathInvalid(const QString& path);
|
||||||
void changeDownloadsPath(const QString& path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::deque<Account*> Accounts;
|
typedef std::deque<Account*> Accounts;
|
||||||
@ -134,7 +131,7 @@ private:
|
|||||||
AccountsMap amap;
|
AccountsMap amap;
|
||||||
Shared::Availability state;
|
Shared::Availability state;
|
||||||
NetworkAccess network;
|
NetworkAccess network;
|
||||||
bool isInitialized;
|
uint8_t waitingForAccounts;
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
PSE::KWallet kwallet;
|
PSE::KWallet kwallet;
|
||||||
@ -147,7 +144,6 @@ private slots:
|
|||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
bool active,
|
|
||||||
Shared::AccountPassword passwordType
|
Shared::AccountPassword passwordType
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -172,22 +168,22 @@ private slots:
|
|||||||
void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data);
|
void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemoveRoomPresence(const QString& jid, const QString& nick);
|
void onAccountRemoveRoomPresence(const QString& jid, const QString& nick);
|
||||||
void onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void onAccountNeedPassword();
|
|
||||||
|
|
||||||
void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText);
|
void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText);
|
||||||
|
|
||||||
void onWalletOpened(bool success);
|
void onWalletOpened(bool success);
|
||||||
|
void onWalletResponsePassword(const QString& login, const QString& password);
|
||||||
void onWalletRejectPassword(const QString& login);
|
void onWalletRejectPassword(const QString& login);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readSettings();
|
void readSettings();
|
||||||
|
void accountReady();
|
||||||
void parseAccount(
|
void parseAccount(
|
||||||
const QString& login,
|
const QString& login,
|
||||||
const QString& server,
|
const QString& server,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
bool active,
|
|
||||||
Shared::AccountPassword passwordType
|
Shared::AccountPassword passwordType
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
target_sources(squawk PRIVATE
|
|
||||||
archive.cpp
|
|
||||||
archive.h
|
|
||||||
storage.cpp
|
|
||||||
storage.h
|
|
||||||
urlstorage.cpp
|
|
||||||
urlstorage.h
|
|
||||||
)
|
|
@ -28,6 +28,5 @@ Utils::Exception::~Exception()
|
|||||||
|
|
||||||
const char* Utils::Exception::what() const noexcept( true )
|
const char* Utils::Exception::what() const noexcept( true )
|
||||||
{
|
{
|
||||||
std::string* msg = new std::string(getMessage());
|
return getMessage().c_str();
|
||||||
return msg->c_str();
|
|
||||||
}
|
}
|
12
external/simpleCrypt/CMakeLists.txt
vendored
12
external/simpleCrypt/CMakeLists.txt
vendored
@ -1,10 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(simplecrypt LANGUAGES CXX)
|
project(simplecrypt)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Core REQUIRED)
|
find_package(Qt5Core CONFIG REQUIRED)
|
||||||
|
|
||||||
add_library(simpleCrypt STATIC simplecrypt.cpp simplecrypt.h)
|
set(simplecrypt_SRC
|
||||||
|
simplecrypt.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tell CMake to create the helloworld executable
|
||||||
|
add_library(simpleCrypt STATIC ${simplecrypt_SRC})
|
||||||
|
|
||||||
|
# Use the Widgets module from Qt 5.
|
||||||
target_link_libraries(simpleCrypt Qt5::Core)
|
target_link_libraries(simpleCrypt Qt5::Core)
|
||||||
|
162
main.cpp
Normal file
162
main.cpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui/squawk.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
#include "signalcatcher.h"
|
||||||
|
#include "shared/global.h"
|
||||||
|
#include "shared/messageinfo.h"
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||||
|
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
||||||
|
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
||||||
|
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
||||||
|
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
||||||
|
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
||||||
|
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
||||||
|
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
SignalCatcher sc(&app);
|
||||||
|
|
||||||
|
QApplication::setApplicationName("squawk");
|
||||||
|
QApplication::setApplicationDisplayName("Squawk");
|
||||||
|
QApplication::setApplicationVersion("0.1.5");
|
||||||
|
|
||||||
|
QTranslator qtTranslator;
|
||||||
|
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||||
|
app.installTranslator(&qtTranslator);
|
||||||
|
|
||||||
|
QTranslator myappTranslator;
|
||||||
|
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
bool found = false;
|
||||||
|
for (QString share : shares) {
|
||||||
|
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.installTranslator(&myappTranslator);
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":images/logo.svg", QSize(16, 16));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(24, 24));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(32, 32));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(48, 48));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(64, 64));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(96, 96));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(128, 128));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(256, 256));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(512, 512));
|
||||||
|
QApplication::setWindowIcon(icon);
|
||||||
|
|
||||||
|
new Shared::Global(); //translates enums
|
||||||
|
|
||||||
|
Squawk w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
Core::Squawk* squawk = new Core::Squawk();
|
||||||
|
QThread* coreThread = new QThread();
|
||||||
|
squawk->moveToThread(coreThread);
|
||||||
|
|
||||||
|
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
||||||
|
QObject::connect(&app, &QApplication::aboutToQuit, squawk, &Core::Squawk::stop);
|
||||||
|
QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit);
|
||||||
|
QObject::connect(coreThread, &QThread::finished, squawk, &Core::Squawk::deleteLater);
|
||||||
|
|
||||||
|
QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest);
|
||||||
|
QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest);
|
||||||
|
QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest);
|
||||||
|
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
||||||
|
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
||||||
|
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
||||||
|
QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage);
|
||||||
|
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
||||||
|
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
||||||
|
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
||||||
|
QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest);
|
||||||
|
QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest);
|
||||||
|
QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined);
|
||||||
|
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
||||||
|
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
||||||
|
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
||||||
|
QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest);
|
||||||
|
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
||||||
|
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
||||||
|
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
||||||
|
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
||||||
|
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
||||||
|
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
||||||
|
QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid);
|
||||||
|
|
||||||
|
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup);
|
||||||
|
QObject::connect(squawk, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&w, qOverload<const QString&, const QString&>(&Squawk::removeContact));
|
||||||
|
QObject::connect(squawk, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&w, qOverload<const QString&, const QString&, const QString&>(&Squawk::removeContact));
|
||||||
|
QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::changeMessage, &w, &Squawk::changeMessage);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
||||||
|
|
||||||
|
coreThread->start();
|
||||||
|
|
||||||
|
int result = app.exec();
|
||||||
|
|
||||||
|
w.writeSettings();
|
||||||
|
coreThread->wait(500); //TODO hate doing that but settings for some reason don't get saved to the disk
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
target_sources(squawk PRIVATE
|
|
||||||
main.cpp
|
|
||||||
application.cpp
|
|
||||||
application.h
|
|
||||||
dialogqueue.cpp
|
|
||||||
dialogqueue.h
|
|
||||||
)
|
|
@ -1,566 +0,0 @@
|
|||||||
// Squawk messenger.
|
|
||||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include "application.h"
|
|
||||||
|
|
||||||
Application::Application(Core::Squawk* p_core):
|
|
||||||
QObject(),
|
|
||||||
availability(Shared::Availability::offline),
|
|
||||||
core(p_core),
|
|
||||||
squawk(nullptr),
|
|
||||||
notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"),
|
|
||||||
roster(),
|
|
||||||
conversations(),
|
|
||||||
dialogueQueue(roster),
|
|
||||||
nowQuitting(false),
|
|
||||||
destroyingSquawk(false),
|
|
||||||
storage()
|
|
||||||
{
|
|
||||||
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
|
|
||||||
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
|
|
||||||
|
|
||||||
|
|
||||||
//connecting myself to the backed
|
|
||||||
connect(this, &Application::changeState, core, &Core::Squawk::changeState);
|
|
||||||
connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined);
|
|
||||||
connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin);
|
|
||||||
connect(this, &Application::subscribeContact, core, &Core::Squawk::subscribeContact);
|
|
||||||
connect(this, &Application::unsubscribeContact, core, &Core::Squawk::unsubscribeContact);
|
|
||||||
connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage);
|
|
||||||
connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage);
|
|
||||||
connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage);
|
|
||||||
connect(&roster, &Models::Roster::requestArchive,
|
|
||||||
std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3));
|
|
||||||
|
|
||||||
connect(&dialogueQueue, &DialogQueue::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
|
||||||
connect(&dialogueQueue, &DialogQueue::responsePassword, core, &Core::Squawk::responsePassword);
|
|
||||||
connect(&dialogueQueue, &DialogQueue::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
|
||||||
|
|
||||||
connect(&roster, &Models::Roster::fileDownloadRequest, core, &Core::Squawk::fileDownloadRequest);
|
|
||||||
connect(&roster, &Models::Roster::localPathInvalid, core, &Core::Squawk::onLocalPathInvalid);
|
|
||||||
|
|
||||||
|
|
||||||
//coonecting backend to myself
|
|
||||||
connect(core, &Core::Squawk::stateChanged, this, &Application::stateChanged);
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::accountMessage, &roster, &Models::Roster::addMessage);
|
|
||||||
connect(core, &Core::Squawk::responseArchive, &roster, &Models::Roster::responseArchive);
|
|
||||||
connect(core, &Core::Squawk::changeMessage, &roster, &Models::Roster::changeMessage);
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::newAccount, &roster, &Models::Roster::addAccount);
|
|
||||||
connect(core, &Core::Squawk::changeAccount, this, &Application::changeAccount);
|
|
||||||
connect(core, &Core::Squawk::removeAccount, this, &Application::removeAccount);
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::addContact, this, &Application::addContact);
|
|
||||||
connect(core, &Core::Squawk::addGroup, this, &Application::addGroup);
|
|
||||||
connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup);
|
|
||||||
connect(core, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
|
||||||
&roster, qOverload<const QString&, const QString&>(&Models::Roster::removeContact));
|
|
||||||
connect(core, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
|
||||||
&roster, qOverload<const QString&, const QString&, const QString&>(&Models::Roster::removeContact));
|
|
||||||
connect(core, &Core::Squawk::changeContact, &roster, &Models::Roster::changeContact);
|
|
||||||
connect(core, &Core::Squawk::addPresence, &roster, &Models::Roster::addPresence);
|
|
||||||
connect(core, &Core::Squawk::removePresence, &roster, &Models::Roster::removePresence);
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::addRoom, &roster, &Models::Roster::addRoom);
|
|
||||||
connect(core, &Core::Squawk::changeRoom, &roster, &Models::Roster::changeRoom);
|
|
||||||
connect(core, &Core::Squawk::removeRoom, &roster, &Models::Roster::removeRoom);
|
|
||||||
connect(core, &Core::Squawk::addRoomParticipant, &roster, &Models::Roster::addRoomParticipant);
|
|
||||||
connect(core, &Core::Squawk::changeRoomParticipant, &roster, &Models::Roster::changeRoomParticipant);
|
|
||||||
connect(core, &Core::Squawk::removeRoomParticipant, &roster, &Models::Roster::removeRoomParticipant);
|
|
||||||
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::fileDownloadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, false));
|
|
||||||
connect(core, &Core::Squawk::fileUploadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, true));
|
|
||||||
connect(core, &Core::Squawk::fileProgress, &roster, &Models::Roster::fileProgress);
|
|
||||||
connect(core, &Core::Squawk::fileError, &roster, &Models::Roster::fileError);
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword);
|
|
||||||
connect(core, &Core::Squawk::ready, this, &Application::readSettings);
|
|
||||||
|
|
||||||
QDBusConnection sys = QDBusConnection::sessionBus();
|
|
||||||
sys.connect(
|
|
||||||
"org.freedesktop.Notifications",
|
|
||||||
"/org/freedesktop/Notifications",
|
|
||||||
"org.freedesktop.Notifications",
|
|
||||||
"NotificationClosed",
|
|
||||||
this,
|
|
||||||
SLOT(onNotificationClosed(quint32, quint32))
|
|
||||||
);
|
|
||||||
sys.connect(
|
|
||||||
"org.freedesktop.Notifications",
|
|
||||||
"/org/freedesktop/Notifications",
|
|
||||||
"org.freedesktop.Notifications",
|
|
||||||
"ActionInvoked",
|
|
||||||
this,
|
|
||||||
SLOT(onNotificationInvoked(quint32, const QString&))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Application::~Application() {}
|
|
||||||
|
|
||||||
void Application::quit()
|
|
||||||
{
|
|
||||||
if (!nowQuitting) {
|
|
||||||
nowQuitting = true;
|
|
||||||
emit quitting();
|
|
||||||
|
|
||||||
writeSettings();
|
|
||||||
unreadMessagesCountChanged(0); //this notification persist in the desktop, for now I'll zero it on quit not to confuse people
|
|
||||||
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
|
|
||||||
disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed);
|
|
||||||
itr->second->close();
|
|
||||||
}
|
|
||||||
conversations.clear();
|
|
||||||
dialogueQueue.quit();
|
|
||||||
|
|
||||||
if (squawk != nullptr) {
|
|
||||||
squawk->close();
|
|
||||||
}
|
|
||||||
if (!destroyingSquawk) {
|
|
||||||
checkForTheLastWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::checkForTheLastWindow()
|
|
||||||
{
|
|
||||||
if (QApplication::topLevelWidgets().size() > 0) {
|
|
||||||
emit readyToQuit();
|
|
||||||
} else {
|
|
||||||
connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::createMainWindow()
|
|
||||||
{
|
|
||||||
if (squawk == nullptr) {
|
|
||||||
squawk = new Squawk(roster);
|
|
||||||
|
|
||||||
connect(squawk, &Squawk::notify, this, &Application::notify);
|
|
||||||
connect(squawk, &Squawk::changeSubscription, this, &Application::changeSubscription);
|
|
||||||
connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation);
|
|
||||||
connect(squawk, &Squawk::openConversation, this, &Application::openConversation);
|
|
||||||
connect(squawk, &Squawk::changeState, this, &Application::setState);
|
|
||||||
connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing);
|
|
||||||
|
|
||||||
connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
|
||||||
connect(squawk, &Squawk::newAccountRequest, core, &Core::Squawk::newAccountRequest);
|
|
||||||
connect(squawk, &Squawk::removeAccountRequest, core, &Core::Squawk::removeAccountRequest);
|
|
||||||
connect(squawk, &Squawk::connectAccount, core, &Core::Squawk::connectAccount);
|
|
||||||
connect(squawk, &Squawk::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
|
||||||
|
|
||||||
connect(squawk, &Squawk::addContactRequest, core, &Core::Squawk::addContactRequest);
|
|
||||||
connect(squawk, &Squawk::removeContactRequest, core, &Core::Squawk::removeContactRequest);
|
|
||||||
connect(squawk, &Squawk::removeRoomRequest, core, &Core::Squawk::removeRoomRequest);
|
|
||||||
connect(squawk, &Squawk::addRoomRequest, core, &Core::Squawk::addRoomRequest);
|
|
||||||
connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest);
|
|
||||||
connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest);
|
|
||||||
connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest);
|
|
||||||
connect(squawk, &Squawk::requestVCard, core, &Core::Squawk::requestVCard);
|
|
||||||
connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard);
|
|
||||||
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
|
|
||||||
|
|
||||||
connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
|
||||||
|
|
||||||
dialogueQueue.setParentWidnow(squawk);
|
|
||||||
squawk->stateChanged(availability);
|
|
||||||
squawk->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onSquawkClosing()
|
|
||||||
{
|
|
||||||
dialogueQueue.setParentWidnow(nullptr);
|
|
||||||
|
|
||||||
if (!nowQuitting) {
|
|
||||||
disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyingSquawk = true;
|
|
||||||
squawk->deleteLater();
|
|
||||||
squawk = nullptr;
|
|
||||||
|
|
||||||
//for now
|
|
||||||
quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onSquawkDestroyed() {
|
|
||||||
destroyingSquawk = false;
|
|
||||||
if (nowQuitting) {
|
|
||||||
checkForTheLastWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Application::notify(const QString& account, const Shared::Message& msg)
|
|
||||||
{
|
|
||||||
QString jid = msg.getPenPalJid();
|
|
||||||
QString name = QString(roster.getContactName(account, jid));
|
|
||||||
QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource()));
|
|
||||||
QVariantList args;
|
|
||||||
args << QString();
|
|
||||||
|
|
||||||
uint32_t notificationId = qHash(msg.getId());
|
|
||||||
args << notificationId;
|
|
||||||
if (path.size() > 0) {
|
|
||||||
args << path;
|
|
||||||
} else {
|
|
||||||
args << QString("mail-message"); //TODO should here better be unknown user icon?
|
|
||||||
}
|
|
||||||
if (msg.getType() == Shared::Message::groupChat) {
|
|
||||||
args << msg.getFromResource() + tr(" from ") + name;
|
|
||||||
} else {
|
|
||||||
args << name;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString body(msg.getBody());
|
|
||||||
QString oob(msg.getOutOfBandUrl());
|
|
||||||
if (body == oob) {
|
|
||||||
body = tr("Attached file");
|
|
||||||
}
|
|
||||||
|
|
||||||
args << body;
|
|
||||||
args << QStringList({
|
|
||||||
"markAsRead", tr("Mark as Read"),
|
|
||||||
"openConversation", tr("Open conversation")
|
|
||||||
});
|
|
||||||
args << QVariantMap({
|
|
||||||
{"desktop-entry", qApp->desktopFileName()},
|
|
||||||
{"category", QString("message")},
|
|
||||||
{"urgency", 1},
|
|
||||||
// {"sound-file", "/path/to/macaw/squawk"},
|
|
||||||
{"sound-name", QString("message-new-instant")}
|
|
||||||
});
|
|
||||||
args << -1;
|
|
||||||
notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
|
|
||||||
|
|
||||||
storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId())));
|
|
||||||
|
|
||||||
if (squawk != nullptr) {
|
|
||||||
QApplication::alert(squawk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onNotificationClosed(quint32 id, quint32 reason)
|
|
||||||
{
|
|
||||||
Notifications::const_iterator itr = storage.find(id);
|
|
||||||
if (itr != storage.end()) {
|
|
||||||
if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html)
|
|
||||||
//TODO may ba also mark as read?
|
|
||||||
}
|
|
||||||
if (reason != 1) { //just expired, can be activated again from history, so no removing for now
|
|
||||||
storage.erase(id);
|
|
||||||
qDebug() << "Notification" << id << "was closed";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onNotificationInvoked(quint32 id, const QString& action)
|
|
||||||
{
|
|
||||||
qDebug() << "Notification" << id << action << "request";
|
|
||||||
Notifications::const_iterator itr = storage.find(id);
|
|
||||||
if (itr != storage.end()) {
|
|
||||||
if (action == "markAsRead") {
|
|
||||||
roster.markMessageAsRead(itr->second.first, itr->second.second);
|
|
||||||
} else if (action == "openConversation") {
|
|
||||||
focusConversation(itr->second.first, "", itr->second.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::unreadMessagesCountChanged(int count)
|
|
||||||
{
|
|
||||||
QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update");
|
|
||||||
signal << qApp->desktopFileName() + QLatin1String(".desktop");
|
|
||||||
signal << QVariantMap ({
|
|
||||||
{"count-visible", count != 0},
|
|
||||||
{"count", count}
|
|
||||||
});
|
|
||||||
QDBusConnection::sessionBus().send(signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId)
|
|
||||||
{
|
|
||||||
if (squawk != nullptr) {
|
|
||||||
if (squawk->currentConversationId() != id) {
|
|
||||||
QModelIndex index = roster.getContactIndex(id.account, id.name, resource);
|
|
||||||
squawk->select(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (squawk->isMinimized()) {
|
|
||||||
squawk->showNormal();
|
|
||||||
} else {
|
|
||||||
squawk->show();
|
|
||||||
}
|
|
||||||
squawk->raise();
|
|
||||||
squawk->activateWindow();
|
|
||||||
} else {
|
|
||||||
openConversation(id, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO focus messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setState(Shared::Availability p_availability)
|
|
||||||
{
|
|
||||||
if (availability != p_availability) {
|
|
||||||
availability = p_availability;
|
|
||||||
emit changeState(availability);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::stateChanged(Shared::Availability state)
|
|
||||||
{
|
|
||||||
availability = state;
|
|
||||||
if (squawk != nullptr) {
|
|
||||||
squawk->stateChanged(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::readSettings()
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
settings.beginGroup("ui");
|
|
||||||
int avail;
|
|
||||||
if (settings.contains("availability")) {
|
|
||||||
avail = settings.value("availability").toInt();
|
|
||||||
} else {
|
|
||||||
avail = static_cast<int>(Shared::Availability::online);
|
|
||||||
}
|
|
||||||
settings.endGroup();
|
|
||||||
|
|
||||||
setState(Shared::Global::fromInt<Shared::Availability>(avail));
|
|
||||||
createMainWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::writeSettings()
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
settings.setValue("availability", static_cast<int>(availability));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::requestPassword(const QString& account, bool authenticationError) {
|
|
||||||
if (authenticationError) {
|
|
||||||
dialogueQueue.addAction(account, DialogQueue::askCredentials);
|
|
||||||
} else {
|
|
||||||
dialogueQueue.addAction(account, DialogQueue::askPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
void Application::onConversationClosed()
|
|
||||||
{
|
|
||||||
Conversation* conv = static_cast<Conversation*>(sender());
|
|
||||||
Models::Roster::ElId id(conv->getAccount(), conv->getJid());
|
|
||||||
Conversations::const_iterator itr = conversations.find(id);
|
|
||||||
if (itr != conversations.end()) {
|
|
||||||
conversations.erase(itr);
|
|
||||||
}
|
|
||||||
if (conv->isMuc) {
|
|
||||||
Room* room = static_cast<Room*>(conv);
|
|
||||||
if (!room->autoJoined()) {
|
|
||||||
emit setRoomJoined(id.account, id.name, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe)
|
|
||||||
{
|
|
||||||
Models::Item::Type type = roster.getContactType(id);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Models::Item::contact:
|
|
||||||
if (subscribe) {
|
|
||||||
emit subscribeContact(id.account, id.name, "");
|
|
||||||
} else {
|
|
||||||
emit unsubscribeContact(id.account, id.name, "");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Models::Item::room:
|
|
||||||
setRoomAutoJoin(id.account, id.name, subscribe);
|
|
||||||
if (!isConverstationOpened(id)) {
|
|
||||||
emit setRoomJoined(id.account, id.name, subscribe);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::subscribeConversation(Conversation* conv)
|
|
||||||
{
|
|
||||||
connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
|
||||||
connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage);
|
|
||||||
connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage);
|
|
||||||
connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend);
|
|
||||||
connect(conv, &Conversation::notifyableMessage, this, &Application::notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::openConversation(const Models::Roster::ElId& id, const QString& resource)
|
|
||||||
{
|
|
||||||
Conversations::const_iterator itr = conversations.find(id);
|
|
||||||
Models::Account* acc = roster.getAccount(id.account);
|
|
||||||
Conversation* conv = nullptr;
|
|
||||||
bool created = false;
|
|
||||||
if (itr != conversations.end()) {
|
|
||||||
conv = itr->second;
|
|
||||||
} else {
|
|
||||||
Models::Element* el = roster.getElement(id);
|
|
||||||
if (el != nullptr) {
|
|
||||||
if (el->type == Models::Item::room) {
|
|
||||||
created = true;
|
|
||||||
Models::Room* room = static_cast<Models::Room*>(el);
|
|
||||||
conv = new Room(acc, room);
|
|
||||||
if (!room->getJoined()) {
|
|
||||||
emit setRoomJoined(id.account, id.name, true);
|
|
||||||
}
|
|
||||||
} else if (el->type == Models::Item::contact) {
|
|
||||||
created = true;
|
|
||||||
conv = new Chat(acc, static_cast<Models::Contact*>(el));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conv != nullptr) {
|
|
||||||
if (created) {
|
|
||||||
conv->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
subscribeConversation(conv);
|
|
||||||
conversations.insert(std::make_pair(id, conv));
|
|
||||||
}
|
|
||||||
|
|
||||||
conv->show();
|
|
||||||
conv->raise();
|
|
||||||
conv->activateWindow();
|
|
||||||
|
|
||||||
if (resource.size() > 0) {
|
|
||||||
conv->setPalResource(resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onConversationMessage(const Shared::Message& msg)
|
|
||||||
{
|
|
||||||
Conversation* conv = static_cast<Conversation*>(sender());
|
|
||||||
QString acc = conv->getAccount();
|
|
||||||
|
|
||||||
roster.addMessage(acc, msg);
|
|
||||||
emit sendMessage(acc, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg)
|
|
||||||
{
|
|
||||||
Conversation* conv = static_cast<Conversation*>(sender());
|
|
||||||
QString acc = conv->getAccount();
|
|
||||||
|
|
||||||
roster.changeMessage(acc, msg.getPenPalJid(), originalId, {
|
|
||||||
{"state", static_cast<uint>(Shared::Message::State::pending)}
|
|
||||||
});
|
|
||||||
emit replaceMessage(acc, originalId, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onConversationResend(const QString& id)
|
|
||||||
{
|
|
||||||
Conversation* conv = static_cast<Conversation*>(sender());
|
|
||||||
QString acc = conv->getAccount();
|
|
||||||
QString jid = conv->getJid();
|
|
||||||
|
|
||||||
emit resendMessage(acc, jid, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::onSquawkOpenedConversation() {
|
|
||||||
subscribeConversation(squawk->currentConversation);
|
|
||||||
Models::Roster::ElId id = squawk->currentConversationId();
|
|
||||||
|
|
||||||
const Models::Element* el = roster.getElementConst(id);
|
|
||||||
if (el != nullptr && el->isRoom() && !static_cast<const Models::Room*>(el)->getJoined()) {
|
|
||||||
emit setRoomJoined(id.account, id.name, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::removeAccount(const QString& account)
|
|
||||||
{
|
|
||||||
Conversations::const_iterator itr = conversations.begin();
|
|
||||||
while (itr != conversations.end()) {
|
|
||||||
if (itr->first.account == account) {
|
|
||||||
Conversations::const_iterator lItr = itr;
|
|
||||||
++itr;
|
|
||||||
Conversation* conv = lItr->second;
|
|
||||||
disconnect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
|
||||||
conv->close();
|
|
||||||
conversations.erase(lItr);
|
|
||||||
} else {
|
|
||||||
++itr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (squawk != nullptr && squawk->currentConversationId().account == account) {
|
|
||||||
squawk->closeCurrentConversation();
|
|
||||||
}
|
|
||||||
|
|
||||||
roster.removeAccount(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::changeAccount(const QString& account, const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
|
||||||
QString attr = itr.key();
|
|
||||||
roster.updateAccount(account, attr, *itr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
roster.addContact(account, jid, group, data);
|
|
||||||
|
|
||||||
if (squawk != nullptr) {
|
|
||||||
QSettings settings;
|
|
||||||
settings.beginGroup("ui");
|
|
||||||
settings.beginGroup("roster");
|
|
||||||
settings.beginGroup(account);
|
|
||||||
if (settings.value("expanded", false).toBool()) {
|
|
||||||
QModelIndex ind = roster.getAccountIndex(account);
|
|
||||||
squawk->expand(ind);
|
|
||||||
}
|
|
||||||
settings.endGroup();
|
|
||||||
settings.endGroup();
|
|
||||||
settings.endGroup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::addGroup(const QString& account, const QString& name)
|
|
||||||
{
|
|
||||||
roster.addGroup(account, name);
|
|
||||||
|
|
||||||
if (squawk != nullptr) {
|
|
||||||
QSettings settings;
|
|
||||||
settings.beginGroup("ui");
|
|
||||||
settings.beginGroup("roster");
|
|
||||||
settings.beginGroup(account);
|
|
||||||
if (settings.value("expanded", false).toBool()) {
|
|
||||||
QModelIndex ind = roster.getAccountIndex(account);
|
|
||||||
squawk->expand(ind);
|
|
||||||
if (settings.value(name + "/expanded", false).toBool()) {
|
|
||||||
squawk->expand(roster.getGroupIndex(account, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
settings.endGroup();
|
|
||||||
settings.endGroup();
|
|
||||||
settings.endGroup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::isConverstationOpened(const Models::Roster::ElId& id) const {
|
|
||||||
return (conversations.count(id) > 0) || (squawk != nullptr && squawk->currentConversationId() == id);}
|
|
@ -1,118 +0,0 @@
|
|||||||
// Squawk messenger.
|
|
||||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#ifndef APPLICATION_H
|
|
||||||
#define APPLICATION_H
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDBusInterface>
|
|
||||||
|
|
||||||
#include "dialogqueue.h"
|
|
||||||
#include "core/squawk.h"
|
|
||||||
|
|
||||||
#include "ui/squawk.h"
|
|
||||||
#include "ui/models/roster.h"
|
|
||||||
#include "ui/widgets/conversation.h"
|
|
||||||
|
|
||||||
#include "shared/message.h"
|
|
||||||
#include "shared/enums.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
class Application : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Application(Core::Squawk* core);
|
|
||||||
~Application();
|
|
||||||
|
|
||||||
bool isConverstationOpened(const Models::Roster::ElId& id) const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void sendMessage(const QString& account, const Shared::Message& data);
|
|
||||||
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
|
||||||
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
|
||||||
|
|
||||||
void changeState(Shared::Availability state);
|
|
||||||
|
|
||||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
|
||||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
|
||||||
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
|
||||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
|
||||||
|
|
||||||
void quitting();
|
|
||||||
void readyToQuit();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void readSettings();
|
|
||||||
void quit();
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void notify(const QString& account, const Shared::Message& msg);
|
|
||||||
void unreadMessagesCountChanged(int count);
|
|
||||||
void setState(Shared::Availability availability);
|
|
||||||
|
|
||||||
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
|
||||||
void removeAccount(const QString& account);
|
|
||||||
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
|
|
||||||
|
|
||||||
void addGroup(const QString& account, const QString& name);
|
|
||||||
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
|
||||||
|
|
||||||
void requestPassword(const QString& account, bool authenticationError);
|
|
||||||
|
|
||||||
void writeSettings();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onConversationClosed();
|
|
||||||
void changeSubscription(const Models::Roster::ElId& id, bool subscribe);
|
|
||||||
void onSquawkOpenedConversation();
|
|
||||||
void onConversationMessage(const Shared::Message& msg);
|
|
||||||
void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg);
|
|
||||||
void onConversationResend(const QString& id);
|
|
||||||
void stateChanged(Shared::Availability state);
|
|
||||||
void onSquawkClosing();
|
|
||||||
void onSquawkDestroyed();
|
|
||||||
void onNotificationClosed(quint32 id, quint32 reason);
|
|
||||||
void onNotificationInvoked(quint32 id, const QString& action);
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
void createMainWindow();
|
|
||||||
void subscribeConversation(Conversation* conv);
|
|
||||||
void checkForTheLastWindow();
|
|
||||||
void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = "");
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
|
||||||
typedef std::map<uint32_t, std::pair<Models::Roster::ElId, QString>> Notifications;
|
|
||||||
|
|
||||||
Shared::Availability availability;
|
|
||||||
Core::Squawk* core;
|
|
||||||
Squawk* squawk;
|
|
||||||
QDBusInterface notifications;
|
|
||||||
Models::Roster roster;
|
|
||||||
Conversations conversations;
|
|
||||||
DialogQueue dialogueQueue;
|
|
||||||
bool nowQuitting;
|
|
||||||
bool destroyingSquawk;
|
|
||||||
Notifications storage;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // APPLICATION_H
|
|
@ -1,187 +0,0 @@
|
|||||||
// Squawk messenger.
|
|
||||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include "dialogqueue.h"
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
DialogQueue::DialogQueue(const Models::Roster& p_roster):
|
|
||||||
QObject(),
|
|
||||||
currentSource(),
|
|
||||||
currentAction(none),
|
|
||||||
queue(),
|
|
||||||
collection(queue.get<0>()),
|
|
||||||
sequence(queue.get<1>()),
|
|
||||||
prompt(nullptr),
|
|
||||||
parent(nullptr),
|
|
||||||
roster(p_roster)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogQueue::~DialogQueue()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogQueue::quit()
|
|
||||||
{
|
|
||||||
queue.clear();
|
|
||||||
if (currentAction != none) {
|
|
||||||
actionDone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogQueue::setParentWidnow(QMainWindow* p_parent)
|
|
||||||
{
|
|
||||||
parent = p_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DialogQueue::addAction(const QString& source, DialogQueue::Action action)
|
|
||||||
{
|
|
||||||
if (action == none) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (currentAction == none) {
|
|
||||||
currentAction = action;
|
|
||||||
currentSource = source;
|
|
||||||
performNextAction();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (currentAction != action || currentSource != source) {
|
|
||||||
std::pair<Queue::iterator, bool> result = queue.emplace(source, action);
|
|
||||||
return result.second;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DialogQueue::cancelAction(const QString& source, DialogQueue::Action action)
|
|
||||||
{
|
|
||||||
if (source == currentSource && action == currentAction) {
|
|
||||||
actionDone();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
Collection::iterator itr = collection.find(ActionId{source, action});
|
|
||||||
if (itr != collection.end()) {
|
|
||||||
collection.erase(itr);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogQueue::performNextAction()
|
|
||||||
{
|
|
||||||
switch (currentAction) {
|
|
||||||
case none:
|
|
||||||
actionDone();
|
|
||||||
break;
|
|
||||||
case askPassword: {
|
|
||||||
QInputDialog* dialog = new QInputDialog(parent);
|
|
||||||
prompt = dialog;
|
|
||||||
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
|
|
||||||
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
|
|
||||||
dialog->setInputMode(QInputDialog::TextInput);
|
|
||||||
dialog->setTextEchoMode(QLineEdit::Password);
|
|
||||||
dialog->setLabelText(tr("Input the password for account %1").arg(currentSource));
|
|
||||||
dialog->setWindowTitle(tr("Password for account %1").arg(currentSource));
|
|
||||||
dialog->setTextValue("");
|
|
||||||
dialog->exec();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case askCredentials: {
|
|
||||||
CredentialsPrompt* dialog = new CredentialsPrompt(parent);
|
|
||||||
prompt = dialog;
|
|
||||||
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
|
|
||||||
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
|
|
||||||
const Models::Account* acc = roster.getAccountConst(currentSource);
|
|
||||||
dialog->setAccount(currentSource);
|
|
||||||
dialog->setLogin(acc->getLogin());
|
|
||||||
dialog->setPassword(acc->getPassword());
|
|
||||||
dialog->exec();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogQueue::onPropmtAccepted()
|
|
||||||
{
|
|
||||||
switch (currentAction) {
|
|
||||||
case none:
|
|
||||||
break;
|
|
||||||
case askPassword: {
|
|
||||||
QInputDialog* dialog = static_cast<QInputDialog*>(prompt);
|
|
||||||
emit responsePassword(currentSource, dialog->textValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case askCredentials: {
|
|
||||||
CredentialsPrompt* dialog = static_cast<CredentialsPrompt*>(prompt);
|
|
||||||
emit modifyAccountRequest(currentSource, {
|
|
||||||
{"login", dialog->getLogin()},
|
|
||||||
{"password", dialog->getPassword()}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
actionDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogQueue::onPropmtRejected()
|
|
||||||
{
|
|
||||||
switch (currentAction) {
|
|
||||||
case none:
|
|
||||||
break;
|
|
||||||
case askPassword:
|
|
||||||
case askCredentials:
|
|
||||||
emit disconnectAccount(currentSource);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
actionDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialogQueue::actionDone()
|
|
||||||
{
|
|
||||||
prompt->deleteLater();
|
|
||||||
prompt = nullptr;
|
|
||||||
|
|
||||||
if (queue.empty()) {
|
|
||||||
currentAction = none;
|
|
||||||
currentSource = "";
|
|
||||||
} else {
|
|
||||||
Sequence::iterator itr = sequence.begin();
|
|
||||||
currentAction = itr->action;
|
|
||||||
currentSource = itr->source;
|
|
||||||
sequence.erase(itr);
|
|
||||||
performNextAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogQueue::ActionId::ActionId(const QString& p_source, DialogQueue::Action p_action):
|
|
||||||
source(p_source),
|
|
||||||
action(p_action) {}
|
|
||||||
|
|
||||||
bool DialogQueue::ActionId::operator < (const DialogQueue::ActionId& other) const
|
|
||||||
{
|
|
||||||
if (action == other.action) {
|
|
||||||
return source < other.source;
|
|
||||||
} else {
|
|
||||||
return action < other.action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogQueue::ActionId::ActionId(const DialogQueue::ActionId& other):
|
|
||||||
source(other.source),
|
|
||||||
action(other.action) {}
|
|
@ -1,101 +0,0 @@
|
|||||||
// Squawk messenger.
|
|
||||||
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#ifndef DIALOGQUEUE_H
|
|
||||||
#define DIALOGQUEUE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QMainWindow>
|
|
||||||
|
|
||||||
#include <boost/multi_index_container.hpp>
|
|
||||||
#include <boost/multi_index/ordered_index.hpp>
|
|
||||||
#include <boost/multi_index/sequenced_index.hpp>
|
|
||||||
|
|
||||||
#include <ui/widgets/accounts/credentialsprompt.h>
|
|
||||||
#include <ui/models/roster.h>
|
|
||||||
|
|
||||||
class DialogQueue : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
enum Action {
|
|
||||||
none,
|
|
||||||
askPassword,
|
|
||||||
askCredentials
|
|
||||||
};
|
|
||||||
|
|
||||||
DialogQueue(const Models::Roster& roster);
|
|
||||||
~DialogQueue();
|
|
||||||
|
|
||||||
bool addAction(const QString& source, Action action);
|
|
||||||
bool cancelAction(const QString& source, Action action);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
|
||||||
void responsePassword(const QString& account, const QString& password);
|
|
||||||
void disconnectAccount(const QString&);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void setParentWidnow(QMainWindow* parent);
|
|
||||||
void quit();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void performNextAction();
|
|
||||||
void actionDone();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onPropmtAccepted();
|
|
||||||
void onPropmtRejected();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString currentSource;
|
|
||||||
Action currentAction;
|
|
||||||
|
|
||||||
struct ActionId {
|
|
||||||
public:
|
|
||||||
ActionId(const QString& p_source, Action p_action);
|
|
||||||
ActionId(const ActionId& other);
|
|
||||||
|
|
||||||
const QString source;
|
|
||||||
const Action action;
|
|
||||||
|
|
||||||
bool operator < (const ActionId& other) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef boost::multi_index_container <
|
|
||||||
ActionId,
|
|
||||||
boost::multi_index::indexed_by <
|
|
||||||
boost::multi_index::ordered_unique <
|
|
||||||
boost::multi_index::identity <ActionId>
|
|
||||||
>,
|
|
||||||
boost::multi_index::sequenced<>
|
|
||||||
>
|
|
||||||
> Queue;
|
|
||||||
|
|
||||||
typedef Queue::nth_index<0>::type Collection;
|
|
||||||
typedef Queue::nth_index<1>::type Sequence;
|
|
||||||
|
|
||||||
Queue queue;
|
|
||||||
Collection& collection;
|
|
||||||
Sequence& sequence;
|
|
||||||
|
|
||||||
QDialog* prompt;
|
|
||||||
QMainWindow* parent;
|
|
||||||
const Models::Roster& roster;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DIALOGQUEUE_H
|
|
140
main/main.cpp
140
main/main.cpp
@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "shared/global.h"
|
|
||||||
#include "shared/messageinfo.h"
|
|
||||||
#include "shared/pathcheck.h"
|
|
||||||
#include "main/application.h"
|
|
||||||
#include "core/signalcatcher.h"
|
|
||||||
#include "core/squawk.h"
|
|
||||||
|
|
||||||
#include <QLibraryInfo>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QTranslator>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QThread>
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
|
||||||
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
|
||||||
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
|
||||||
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
|
||||||
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
|
||||||
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
|
||||||
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
|
||||||
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
|
||||||
SignalCatcher sc(&app);
|
|
||||||
|
|
||||||
QApplication::setApplicationName("squawk");
|
|
||||||
QApplication::setOrganizationName("macaw.me");
|
|
||||||
QApplication::setApplicationDisplayName("Squawk");
|
|
||||||
QApplication::setApplicationVersion("0.2.2");
|
|
||||||
app.setDesktopFileName("squawk");
|
|
||||||
|
|
||||||
QTranslator qtTranslator;
|
|
||||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
|
||||||
app.installTranslator(&qtTranslator);
|
|
||||||
|
|
||||||
QTranslator myappTranslator;
|
|
||||||
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
|
||||||
bool found = false;
|
|
||||||
for (QString share : shares) {
|
|
||||||
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
|
||||||
if (found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
app.installTranslator(&myappTranslator);
|
|
||||||
|
|
||||||
QIcon icon;
|
|
||||||
icon.addFile(":images/logo.svg", QSize(16, 16));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(24, 24));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(32, 32));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(48, 48));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(64, 64));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(96, 96));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(128, 128));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(256, 256));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(512, 512));
|
|
||||||
QApplication::setWindowIcon(icon);
|
|
||||||
|
|
||||||
new Shared::Global(); //translates enums
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
QVariant vs = settings.value("style");
|
|
||||||
if (vs.isValid()) {
|
|
||||||
QString style = vs.toString().toLower();
|
|
||||||
if (style != "system") {
|
|
||||||
Shared::Global::setStyle(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Shared::Global::supported("colorSchemeTools")) {
|
|
||||||
QVariant vt = settings.value("theme");
|
|
||||||
if (vt.isValid()) {
|
|
||||||
QString theme = vt.toString();
|
|
||||||
if (theme.toLower() != "system") {
|
|
||||||
Shared::Global::setTheme(theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString path = Shared::downloadsPathCheck();
|
|
||||||
if (path.size() > 0) {
|
|
||||||
settings.setValue("downloadsPath", path);
|
|
||||||
} else {
|
|
||||||
qDebug() << "couldn't initialize directory for downloads, quitting";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Squawk* squawk = new Core::Squawk();
|
|
||||||
QThread* coreThread = new QThread();
|
|
||||||
squawk->moveToThread(coreThread);
|
|
||||||
|
|
||||||
Application application(squawk);
|
|
||||||
|
|
||||||
QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit);
|
|
||||||
|
|
||||||
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
|
||||||
QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop);
|
|
||||||
//QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
|
|
||||||
QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
coreThread->start();
|
|
||||||
int result = app.exec();
|
|
||||||
|
|
||||||
if (coreThread->isRunning()) {
|
|
||||||
//coreThread->wait();
|
|
||||||
//todo if I uncomment that, the app will not quit if it has reconnected at least once
|
|
||||||
//it feels like a symptom of something badly desinged in the core thread
|
|
||||||
//need to investigate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +1,17 @@
|
|||||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||||
pkgname=squawk
|
pkgname=squawk
|
||||||
pkgver=0.2.2
|
pkgver=0.1.5
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
||||||
arch=('i686' 'x86_64')
|
arch=('i686' 'x86_64')
|
||||||
url="https://git.macaw.me/blue/squawk"
|
url="https://git.macaw.me/blue/squawk"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
|
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
|
||||||
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost')
|
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
|
||||||
optdepends=('kwallet: secure password storage (requires rebuild)'
|
optdepends=('kwallet: secure password storage (requires rebuild)')
|
||||||
'kconfig: system themes support (requires rebuild)'
|
|
||||||
'kconfigwidgets: system themes support (requires rebuild)'
|
|
||||||
'kio: better show in folder action (requires rebuild)')
|
|
||||||
|
|
||||||
source=("$pkgname-$pkgver.tar.gz")
|
source=("$pkgname-$pkgver.tar.gz")
|
||||||
sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e')
|
sha256sums=('e1a4c88be9f0481d2aa21078faf42fd0e9d66b490b6d8af82827d441cb58df25')
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||||
@ -22,5 +19,5 @@ build() {
|
|||||||
}
|
}
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
DESTDIR="$pkgdir/" cmake --build . --target install
|
DESTDIR="$pkgdir/" cmake --build . --target install
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
configure_file(squawk.desktop squawk.desktop COPYONLY)
|
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
|
@ -5,10 +5,8 @@ Version=1.0
|
|||||||
Name=Squawk
|
Name=Squawk
|
||||||
GenericName=Instant Messenger
|
GenericName=Instant Messenger
|
||||||
GenericName[ru]=Мгновенные сообщения
|
GenericName[ru]=Мгновенные сообщения
|
||||||
GenericName[pt_BR]=Mensageiro instantâneo
|
|
||||||
Comment=XMPP (Jabber) instant messenger client
|
Comment=XMPP (Jabber) instant messenger client
|
||||||
Comment[ru]=XMPP (Jabber) клиент обмена мгновенными сообщениями
|
Comment[ru]=XMPP (Jabber) клиент обмена мгновенными сообщениями
|
||||||
Comment[pt_BR]=Cliente de mensagem instantânea XMPP (Jabber)
|
|
||||||
Exec=squawk %u
|
Exec=squawk %u
|
||||||
Icon=squawk
|
Icon=squawk
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
|
@ -1,14 +1,26 @@
|
|||||||
if (WITH_KIO)
|
cmake_minimum_required(VERSION 3.3)
|
||||||
add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp)
|
project(plugins)
|
||||||
target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets)
|
|
||||||
|
|
||||||
install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
if (WITH_KIO)
|
||||||
endif ()
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
|
find_package(Qt5Core CONFIG REQUIRED)
|
||||||
|
|
||||||
if (WITH_KCONFIG)
|
set(openFileManagerWindowJob_SRC
|
||||||
add_library(colorSchemeTools SHARED colorschemetools.cpp)
|
openfilemanagerwindowjob.cpp
|
||||||
target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore)
|
)
|
||||||
target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets)
|
|
||||||
|
|
||||||
install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
add_library(openFileManagerWindowJob SHARED ${openFileManagerWindowJob_SRC})
|
||||||
|
|
||||||
|
get_target_property(Qt5CORE_INTERFACE_INCLUDE_DIRECTORIES Qt5::Core INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
get_target_property(KIO_WIDGETS_INTERFACE_INCLUDE_DIRECTORIES KF5::KIOWidgets INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
get_target_property(CORE_ADDONS_INTERFACE_INCLUDE_DIRECTORIES KF5::CoreAddons INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
target_include_directories(openFileManagerWindowJob PUBLIC ${KIO_WIDGETS_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
target_include_directories(openFileManagerWindowJob PUBLIC ${CORE_ADDONS_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
target_include_directories(openFileManagerWindowJob PUBLIC ${Qt5CORE_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
|
||||||
|
target_link_libraries(openFileManagerWindowJob KF5::KIOWidgets)
|
||||||
|
target_link_libraries(openFileManagerWindowJob Qt5::Core)
|
||||||
|
|
||||||
|
install(TARGETS openFileManagerWindowJob DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QFileInfo>
|
|
||||||
|
|
||||||
#include <KConfigCore/KSharedConfig>
|
|
||||||
#include <KConfigCore/KConfigGroup>
|
|
||||||
#include <KConfigWidgets/KColorScheme>
|
|
||||||
|
|
||||||
QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection);
|
|
||||||
|
|
||||||
extern "C" QIcon* createPreview(const QString& path) {
|
|
||||||
KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path);
|
|
||||||
QIcon* result = new QIcon();
|
|
||||||
KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig);
|
|
||||||
KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig);
|
|
||||||
KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig);
|
|
||||||
KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig);
|
|
||||||
|
|
||||||
result->addPixmap(createPixmap(16, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background()));
|
|
||||||
result->addPixmap(createPixmap(24, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background()));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void deletePreview(QIcon* icon) {
|
|
||||||
delete icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void colorSchemeName(const QString& path, QString& answer) {
|
|
||||||
KSharedConfigPtr config = KSharedConfig::openConfig(path);
|
|
||||||
KConfigGroup group(config, QStringLiteral("General"));
|
|
||||||
answer = group.readEntry("Name", QFileInfo(path).baseName());
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void createPalette(const QString& path, QPalette& answer) {
|
|
||||||
KSharedConfigPtr config = KSharedConfig::openConfig(path);
|
|
||||||
answer = KColorScheme::createApplicationPalette(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection) {
|
|
||||||
QPixmap pix(size, size);
|
|
||||||
pix.fill(Qt::black);
|
|
||||||
QPainter p;
|
|
||||||
p.begin(&pix);
|
|
||||||
const int itemSize = size / 2 - 1;
|
|
||||||
p.fillRect(1, 1, itemSize, itemSize, window);
|
|
||||||
p.fillRect(1 + itemSize, 1, itemSize, itemSize, button);
|
|
||||||
p.fillRect(1, 1 + itemSize, itemSize, itemSize, view);
|
|
||||||
p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, selection);
|
|
||||||
p.end();
|
|
||||||
return pix;
|
|
||||||
}
|
|
@ -1,21 +1,3 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <KIO/OpenFileManagerWindowJob>
|
#include <KIO/OpenFileManagerWindowJob>
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
target_sources(squawk PRIVATE resources.qrc)
|
|
||||||
|
|
||||||
configure_file(images/logo.svg squawk.svg COPYONLY)
|
|
||||||
configure_file(squawk.rc squawk.rc COPYONLY)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set(CONVERT_BIN magick convert)
|
|
||||||
else(WIN32)
|
|
||||||
set(CONVERT_BIN convert)
|
|
||||||
endif(WIN32)
|
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
execute_process(COMMAND ${CONVERT_BIN} squawk48.png squawk64.png squawk256.png squawk.ico WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc")
|
|
||||||
set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE)
|
|
||||||
target_sources(squawk PRIVATE ${SQUAWK_WIN_RC})
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset")
|
|
||||||
execute_process(COMMAND convert -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
set(MACOSX_BUNDLE_ICON_FILE squawk.icns)
|
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE)
|
|
||||||
set(APP_ICON_MACOSX ${CMAKE_CURRENT_BINARY_DIR}/squawk.icns)
|
|
||||||
set(APP_ICON_MACOSX ${APP_ICON_MACOSX} PARENT_SCOPE)
|
|
||||||
target_sources(squawk PRIVATE ${APP_ICON_MACOSX})
|
|
||||||
set_source_files_properties(${APP_ICON_MACOSX} TARGET_DIRECTORY squawk PROPERTIES
|
|
||||||
MACOSX_PACKAGE_LOCATION "Resources")
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
if (APPLE)
|
|
||||||
set_target_properties(squawk PROPERTIES
|
|
||||||
MACOSX_BUNDLE_EXECUTABLE_NAME "Squawk"
|
|
||||||
MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO
|
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "Squawk"
|
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in)
|
|
||||||
endif(APPLE)
|
|
||||||
endif()
|
|
||||||
endif (APPLE)
|
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
IDI_ICON1 ICON "squawk.ico"
|
|
@ -19,12 +19,12 @@
|
|||||||
#ifndef SHARED_H
|
#ifndef SHARED_H
|
||||||
#define SHARED_H
|
#define SHARED_H
|
||||||
|
|
||||||
#include "enums.h"
|
#include "shared/enums.h"
|
||||||
#include "global.h"
|
#include "shared/utils.h"
|
||||||
#include "icons.h"
|
#include "shared/icons.h"
|
||||||
#include "message.h"
|
#include "shared/message.h"
|
||||||
#include "messageinfo.h"
|
#include "shared/vcard.h"
|
||||||
#include "utils.h"
|
#include "shared/global.h"
|
||||||
#include "vcard.h"
|
#include "shared/messageinfo.h"
|
||||||
|
|
||||||
#endif // SHARED_H
|
#endif // SHARED_H
|
@ -1,21 +0,0 @@
|
|||||||
target_sources(squawk PRIVATE
|
|
||||||
enums.h
|
|
||||||
global.cpp
|
|
||||||
global.h
|
|
||||||
exception.cpp
|
|
||||||
exception.h
|
|
||||||
icons.cpp
|
|
||||||
icons.h
|
|
||||||
message.cpp
|
|
||||||
message.h
|
|
||||||
messageinfo.cpp
|
|
||||||
messageinfo.h
|
|
||||||
order.h
|
|
||||||
shared.h
|
|
||||||
utils.cpp
|
|
||||||
utils.h
|
|
||||||
vcard.cpp
|
|
||||||
vcard.h
|
|
||||||
pathcheck.cpp
|
|
||||||
pathcheck.h
|
|
||||||
)
|
|
@ -19,7 +19,6 @@
|
|||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
#include "enums.h"
|
#include "enums.h"
|
||||||
#include "ui/models/roster.h"
|
|
||||||
|
|
||||||
Shared::Global* Shared::Global::instance = 0;
|
Shared::Global* Shared::Global::instance = 0;
|
||||||
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
||||||
@ -29,14 +28,6 @@ QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob");
|
|||||||
Shared::Global::HighlightInFileManager Shared::Global::hfm = 0;
|
Shared::Global::HighlightInFileManager Shared::Global::hfm = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_KCONFIG
|
|
||||||
QLibrary Shared::Global::colorSchemeTools("colorSchemeTools");
|
|
||||||
Shared::Global::CreatePreview Shared::Global::createPreview = 0;
|
|
||||||
Shared::Global::DeletePreview Shared::Global::deletePreview = 0;
|
|
||||||
Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0;
|
|
||||||
Shared::Global::CreatePalette Shared::Global::createPalette = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Shared::Global::Global():
|
Shared::Global::Global():
|
||||||
availability({
|
availability({
|
||||||
tr("Online", "Availability"),
|
tr("Online", "Availability"),
|
||||||
@ -93,12 +84,9 @@ Shared::Global::Global():
|
|||||||
tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"),
|
tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"),
|
||||||
tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription")
|
tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription")
|
||||||
}),
|
}),
|
||||||
defaultSystemStyle(QApplication::style()->objectName()),
|
|
||||||
defaultSystemPalette(QApplication::palette()),
|
|
||||||
pluginSupport({
|
pluginSupport({
|
||||||
{"KWallet", false},
|
{"KWallet", false},
|
||||||
{"openFileManagerWindowJob", false},
|
{"openFileManagerWindowJob", false}
|
||||||
{"colorSchemeTools", false}
|
|
||||||
}),
|
}),
|
||||||
fileCache()
|
fileCache()
|
||||||
{
|
{
|
||||||
@ -107,7 +95,7 @@ Shared::Global::Global():
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
#ifdef WITH_KIO
|
#ifdef WITH_KIO
|
||||||
openFileManagerWindowJob.load();
|
openFileManagerWindowJob.load();
|
||||||
if (openFileManagerWindowJob.isLoaded()) {
|
if (openFileManagerWindowJob.isLoaded()) {
|
||||||
@ -122,28 +110,8 @@ Shared::Global::Global():
|
|||||||
qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't load the library" << openFileManagerWindowJob.errorString();
|
qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't load the library" << openFileManagerWindowJob.errorString();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_KCONFIG
|
|
||||||
colorSchemeTools.load();
|
|
||||||
if (colorSchemeTools.isLoaded()) {
|
|
||||||
createPreview = (CreatePreview) colorSchemeTools.resolve("createPreview");
|
|
||||||
deletePreview = (DeletePreview) colorSchemeTools.resolve("deletePreview");
|
|
||||||
colorSchemeName = (ColorSchemeName) colorSchemeTools.resolve("colorSchemeName");
|
|
||||||
createPalette = (CreatePalette) colorSchemeTools.resolve("createPalette");
|
|
||||||
if (createPreview && deletePreview && colorSchemeName && createPalette) {
|
|
||||||
setSupported("colorSchemeTools", true);
|
|
||||||
qDebug() << "Color Schemes support enabled";
|
|
||||||
} else {
|
|
||||||
qDebug() << "Color Schemes support disabled: couldn't resolve required methods in the library";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Color Schemes support disabled: couldn't load the library" << colorSchemeTools.errorString();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const QSize defaultIconFileInfoHeight(50, 50);
|
|
||||||
Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
|
Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
|
||||||
{
|
{
|
||||||
std::map<QString, FileInfo>::const_iterator itr = instance->fileCache.find(path);
|
std::map<QString, FileInfo>::const_iterator itr = instance->fileCache.find(path);
|
||||||
@ -157,24 +125,15 @@ Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
|
|||||||
FileInfo::Preview p = FileInfo::Preview::none;
|
FileInfo::Preview p = FileInfo::Preview::none;
|
||||||
QSize size;
|
QSize size;
|
||||||
if (big == "image") {
|
if (big == "image") {
|
||||||
QMovie mov(path);
|
if (parts.back() == "gif") {
|
||||||
if (mov.isValid() && mov.frameCount() > 1) {
|
//TODO need to consider GIF as a movie
|
||||||
p = FileInfo::Preview::animation;
|
|
||||||
} else {
|
|
||||||
p = FileInfo::Preview::picture;
|
|
||||||
}
|
}
|
||||||
QImageReader img(path);
|
p = FileInfo::Preview::picture;
|
||||||
|
QImage img(path);
|
||||||
size = img.size();
|
size = img.size();
|
||||||
// } else if (big == "video") {
|
|
||||||
// p = FileInfo::Preview::movie;
|
|
||||||
// QMovie mov(path);
|
|
||||||
// size = mov.scaledSize();
|
|
||||||
// qDebug() << mov.isValid();
|
|
||||||
} else {
|
|
||||||
size = defaultIconFileInfoHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.absoluteFilePath(), info.fileName(), size, type, p}))).first;
|
itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.fileName(), size, type, p}))).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return itr->second;
|
return itr->second;
|
||||||
@ -305,50 +264,6 @@ void Shared::Global::highlightInFileManager(const QString& path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon Shared::Global::createThemePreview(const QString& path)
|
|
||||||
{
|
|
||||||
if (supported("colorSchemeTools")) {
|
|
||||||
QIcon* icon = createPreview(path);
|
|
||||||
QIcon localIcon = *icon;
|
|
||||||
deletePreview(icon);
|
|
||||||
return localIcon;
|
|
||||||
} else {
|
|
||||||
return QIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Global::getColorSchemeName(const QString& path)
|
|
||||||
{
|
|
||||||
if (supported("colorSchemeTools")) {
|
|
||||||
QString res;
|
|
||||||
colorSchemeName(path, res);
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Global::setTheme(const QString& path)
|
|
||||||
{
|
|
||||||
if (supported("colorSchemeTools")) {
|
|
||||||
if (path.toLower() == "system") {
|
|
||||||
QApplication::setPalette(getInstance()->defaultSystemPalette);
|
|
||||||
} else {
|
|
||||||
QPalette pallete;
|
|
||||||
createPalette(path, pallete);
|
|
||||||
QApplication::setPalette(pallete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Global::setStyle(const QString& style)
|
|
||||||
{
|
|
||||||
if (style.toLower() == "system") {
|
|
||||||
QApplication::setStyle(getInstance()->defaultSystemStyle);
|
|
||||||
} else {
|
|
||||||
QApplication::setStyle(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define FROM_INT_INPL(Enum) \
|
#define FROM_INT_INPL(Enum) \
|
||||||
template<> \
|
template<> \
|
||||||
|
@ -27,14 +27,12 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStyle>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMimeType>
|
#include <QMimeType>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QMovie>
|
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QLibrary>
|
#include <QLibrary>
|
||||||
@ -47,15 +45,15 @@ namespace Shared {
|
|||||||
|
|
||||||
class Global {
|
class Global {
|
||||||
Q_DECLARE_TR_FUNCTIONS(Global)
|
Q_DECLARE_TR_FUNCTIONS(Global)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct FileInfo {
|
struct FileInfo {
|
||||||
enum class Preview {
|
enum class Preview {
|
||||||
none,
|
none,
|
||||||
picture,
|
picture,
|
||||||
animation
|
movie
|
||||||
};
|
};
|
||||||
|
|
||||||
QString path;
|
|
||||||
QString name;
|
QString name;
|
||||||
QSize size;
|
QSize size;
|
||||||
QMimeType mime;
|
QMimeType mime;
|
||||||
@ -63,7 +61,7 @@ namespace Shared {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Global();
|
Global();
|
||||||
|
|
||||||
static Global* getInstance();
|
static Global* getInstance();
|
||||||
static QString getName(Availability av);
|
static QString getName(Availability av);
|
||||||
static QString getName(ConnectionState cs);
|
static QString getName(ConnectionState cs);
|
||||||
@ -84,9 +82,6 @@ namespace Shared {
|
|||||||
const std::deque<QString> accountPassword;
|
const std::deque<QString> accountPassword;
|
||||||
|
|
||||||
const std::deque<QString> accountPasswordDescription;
|
const std::deque<QString> accountPasswordDescription;
|
||||||
|
|
||||||
const QString defaultSystemStyle;
|
|
||||||
const QPalette defaultSystemPalette;
|
|
||||||
|
|
||||||
static bool supported(const QString& pluginName);
|
static bool supported(const QString& pluginName);
|
||||||
static void setSupported(const QString& pluginName, bool support);
|
static void setSupported(const QString& pluginName, bool support);
|
||||||
@ -95,10 +90,6 @@ namespace Shared {
|
|||||||
|
|
||||||
static FileInfo getFileInfo(const QString& path);
|
static FileInfo getFileInfo(const QString& path);
|
||||||
static void highlightInFileManager(const QString& path);
|
static void highlightInFileManager(const QString& path);
|
||||||
static QIcon createThemePreview(const QString& path);
|
|
||||||
static QString getColorSchemeName(const QString& path);
|
|
||||||
static void setTheme(const QString& path);
|
|
||||||
static void setStyle(const QString& style);
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T fromInt(int src);
|
static T fromInt(int src);
|
||||||
@ -132,20 +123,6 @@ namespace Shared {
|
|||||||
|
|
||||||
static HighlightInFileManager hfm;
|
static HighlightInFileManager hfm;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_KCONFIG
|
|
||||||
static QLibrary colorSchemeTools;
|
|
||||||
|
|
||||||
typedef QIcon* (*CreatePreview)(const QString&);
|
|
||||||
typedef void (*DeletePreview)(QIcon*);
|
|
||||||
typedef void (*ColorSchemeName)(const QString&, QString&);
|
|
||||||
typedef void (*CreatePalette)(const QString&, QPalette&);
|
|
||||||
|
|
||||||
static CreatePreview createPreview;
|
|
||||||
static DeletePreview deletePreview;
|
|
||||||
static ColorSchemeName colorSchemeName;
|
|
||||||
static CreatePalette createPalette;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,11 +404,9 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
|||||||
correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied
|
correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied
|
||||||
}
|
}
|
||||||
if (!edited || lastModified < correctionDate) {
|
if (!edited || lastModified < correctionDate) {
|
||||||
if (!edited) {
|
originalMessage = body;
|
||||||
originalMessage = body;
|
|
||||||
}
|
|
||||||
lastModified = correctionDate;
|
lastModified = correctionDate;
|
||||||
setBody(b);
|
setBody(body);
|
||||||
setEdited(true);
|
setEdited(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "pathcheck.h"
|
|
||||||
|
|
||||||
QRegularExpression squawk("^squawk:\\/\\/");
|
|
||||||
QString Shared::downloadsPathCheck()
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
QVariant dpv = settings.value("downloadsPath");
|
|
||||||
QString path;
|
|
||||||
if (!dpv.isValid()) {
|
|
||||||
path = defaultDownloadsPath();
|
|
||||||
qDebug() << "no downloadsPath variable in config, using default" << path;
|
|
||||||
path = getAbsoluteWritablePath(path);
|
|
||||||
return path;
|
|
||||||
} else {
|
|
||||||
path = dpv.toString();
|
|
||||||
path = getAbsoluteWritablePath(path);
|
|
||||||
if (path.size() == 0) {
|
|
||||||
path = defaultDownloadsPath();
|
|
||||||
qDebug() << "falling back to the default downloads path" << path;
|
|
||||||
path = getAbsoluteWritablePath(path);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::defaultDownloadsPath()
|
|
||||||
{
|
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + QApplication::applicationName();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::getAbsoluteWritablePath(const QString& path)
|
|
||||||
{
|
|
||||||
QDir location(path);
|
|
||||||
if (!location.exists()) {
|
|
||||||
bool res = location.mkpath(location.absolutePath());
|
|
||||||
if (!res) {
|
|
||||||
qDebug() << "couldn't create directory" << path;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QFileInfo info(location.absolutePath());
|
|
||||||
if (info.isWritable()) {
|
|
||||||
return location.absolutePath();
|
|
||||||
} else {
|
|
||||||
qDebug() << "directory" << path << "is not writable";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::resolvePath(QString path)
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
QVariant dpv = settings.value("downloadsPath");
|
|
||||||
return path.replace(squawk, dpv.toString() + "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::squawkifyPath(QString path)
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
QString current = settings.value("downloadsPath").toString();
|
|
||||||
|
|
||||||
if (path.startsWith(current)) {
|
|
||||||
path.replace(0, current.size() + 1, "squawk://");
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::isSubdirectoryOfSettings(const QString& path)
|
|
||||||
{
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
QDir oldPath(settings.value("downloadsPath").toString());
|
|
||||||
QDir newPath(path);
|
|
||||||
|
|
||||||
return newPath.canonicalPath().startsWith(oldPath.canonicalPath());
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PATHCHECK_H
|
|
||||||
#define PATHCHECK_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
namespace Shared {
|
|
||||||
|
|
||||||
QString downloadsPathCheck();
|
|
||||||
QString downloadsPathCheck(QString path);
|
|
||||||
QString defaultDownloadsPath();
|
|
||||||
|
|
||||||
QString getAbsoluteWritablePath(const QString& path);
|
|
||||||
QString resolvePath(QString path);
|
|
||||||
QString squawkifyPath(QString path);
|
|
||||||
bool isSubdirectoryOfSettings(const QString& path);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // PATHCHECK_H
|
|
@ -17,11 +17,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <QUuid>
|
|
||||||
|
|
||||||
QString Shared::generateUUID()
|
QString Shared::generateUUID()
|
||||||
{
|
{
|
||||||
return QUuid::createUuid().toString();
|
uuid_t uuid;
|
||||||
|
uuid_generate(uuid);
|
||||||
|
|
||||||
|
char uuid_str[36];
|
||||||
|
uuid_unparse_lower(uuid, uuid_str);
|
||||||
|
return uuid_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -40,5 +44,5 @@ QString Shared::processMessageBody(const QString& msg)
|
|||||||
{
|
{
|
||||||
QString processed = msg.toHtmlEscaped();
|
QString processed = msg.toHtmlEscaped();
|
||||||
processed.replace(urlReg, "<a href=\"\\1\">\\1</a>");
|
processed.replace(urlReg, "<a href=\"\\1\">\\1</a>");
|
||||||
return "<p style=\"white-space: pre-wrap; line-height: 1em;\">" + processed + "</p>";
|
return "<p style=\"white-space: pre-wrap;\">" + processed + "</p>";
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
//#include "KIO/OpenFileManagerWindowJob"
|
||||||
|
|
||||||
|
#include <uuid/uuid.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Shared {
|
namespace Shared {
|
||||||
@ -69,12 +72,6 @@ static const std::vector<QColor> colorPalette = {
|
|||||||
QColor(17, 17, 80),
|
QColor(17, 17, 80),
|
||||||
QColor(54, 54, 94)
|
QColor(54, 54, 94)
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Hover {
|
|
||||||
nothing,
|
|
||||||
text,
|
|
||||||
anchor
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SHARED_UTILS_H
|
#endif // SHARED_UTILS_H
|
||||||
|
@ -50,7 +50,7 @@ void SignalCatcher::handleSigInt()
|
|||||||
char tmp;
|
char tmp;
|
||||||
ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp));
|
ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp));
|
||||||
|
|
||||||
emit interrupt();
|
app->quit();
|
||||||
|
|
||||||
snInt->setEnabled(true);
|
snInt->setEnabled(true);
|
||||||
}
|
}
|
@ -33,9 +33,6 @@ public:
|
|||||||
|
|
||||||
static void intSignalHandler(int unused);
|
static void intSignalHandler(int unused);
|
||||||
|
|
||||||
signals:
|
|
||||||
void interrupt();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleSigInt();
|
void handleSigInt();
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
find_package(Qt5LinguistTools)
|
|
||||||
|
|
||||||
set(TS_FILES
|
|
||||||
squawk.en.ts
|
|
||||||
squawk.ru.ts
|
|
||||||
squawk.pt_BR.ts
|
|
||||||
)
|
|
||||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
|
||||||
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
|
||||||
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk/l10n)
|
|
||||||
|
|
||||||
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,108 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="ru_RU">
|
<TS version="2.1" language="ru_RU">
|
||||||
<context>
|
|
||||||
<name>About</name>
|
|
||||||
<message>
|
|
||||||
<source>About Squawk</source>
|
|
||||||
<translation>О Программе Squawk</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Squawk</source>
|
|
||||||
<translation>Squawk</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>About</source>
|
|
||||||
<translatorcomment>Tab title</translatorcomment>
|
|
||||||
<translation>Общее</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>XMPP (jabber) messenger</source>
|
|
||||||
<translation>XMPP (jabber) мессенджер</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>(c) 2019 - 2022, Yury Gubich</source>
|
|
||||||
<translation>(c) 2019 - 2022, Юрий Губич</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source><a href="https://git.macaw.me/blue/squawk">Project site</a></source>
|
|
||||||
<translation><a href="https://git.macaw.me/blue/squawk">Сайт проекта</a></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a></source>
|
|
||||||
<translation><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Лицензия: GNU General Public License версия 3</a></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Components</source>
|
|
||||||
<translation>Компоненты</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Version</source>
|
|
||||||
<translation>Версия</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>0.0.0</source>
|
|
||||||
<translation>0.0.0</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Report Bugs</source>
|
|
||||||
<translation>Сообщать об ошибках</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Please report any bug you find!
|
|
||||||
To report bugs you can use:</source>
|
|
||||||
<translation>Пожалуйста, сообщайте о любых ошибках!
|
|
||||||
Способы сообщить об ошибках:</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source><a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</></source>
|
|
||||||
<translation><a href="https://git.macaw.me/blue/squawk/issues">Баг-трекер проекта</></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</source>
|
|
||||||
<translation>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</source>
|
|
||||||
<translation>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Thanks To</source>
|
|
||||||
<translation>Благодарности</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Vae</source>
|
|
||||||
<translation>Vae</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Major refactoring, bug fixes, constructive criticism</source>
|
|
||||||
<translation>Крупный рефакторинг, исправление ошибок, конструктивная критика</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Shunf4</source>
|
|
||||||
<translation>Shunf4</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Major refactoring, bug fixes, build adaptations for Windows and MacOS</source>
|
|
||||||
<translation>Крупный рефакторинг, исправление ошибок, адаптация сборки под Windows and MacOS</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Bruno F. Fontes</source>
|
|
||||||
<translation>Bruno F. Fontes</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Brazilian Portuguese translation</source>
|
|
||||||
<translation>Перевод на Португальский (Бразилия)</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>(built against %1)</source>
|
|
||||||
<translation>(версия при сборке %1)</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>License</source>
|
|
||||||
<translation>Лицензия</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
<context>
|
||||||
<name>Account</name>
|
<name>Account</name>
|
||||||
<message>
|
<message>
|
||||||
@ -112,12 +10,10 @@ To report bugs you can use:</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Your account login</source>
|
<source>Your account login</source>
|
||||||
<translatorcomment>Tooltip</translatorcomment>
|
|
||||||
<translation>Имя пользователя Вашей учетной записи</translation>
|
<translation>Имя пользователя Вашей учетной записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>john_smith1987</source>
|
<source>john_smith1987</source>
|
||||||
<translatorcomment>Login placeholder</translatorcomment>
|
|
||||||
<translation>ivan_ivanov1987</translation>
|
<translation>ivan_ivanov1987</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -126,12 +22,10 @@ To report bugs you can use:</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>A server address of your account. Like 404.city or macaw.me</source>
|
<source>A server address of your account. Like 404.city or macaw.me</source>
|
||||||
<translatorcomment>Tooltip</translatorcomment>
|
|
||||||
<translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation>
|
<translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>macaw.me</source>
|
<source>macaw.me</source>
|
||||||
<translatorcomment>Placeholder</translatorcomment>
|
|
||||||
<translation>macaw.me</translation>
|
<translation>macaw.me</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -144,7 +38,6 @@ To report bugs you can use:</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password of your account</source>
|
<source>Password of your account</source>
|
||||||
<translatorcomment>Tooltip</translatorcomment>
|
|
||||||
<translation>Пароль вашей учетной записи</translation>
|
<translation>Пароль вашей учетной записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -153,11 +46,10 @@ To report bugs you can use:</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Just a name how would you call this account, doesn't affect anything</source>
|
<source>Just a name how would you call this account, doesn't affect anything</source>
|
||||||
<translation>Просто имя, то как Вы называете свою учетную запись, может быть любым (нельзя поменять)</translation>
|
<translation>Просто имя, то как Вы называете свою учетную запись, может быть любым</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>John</source>
|
<source>John</source>
|
||||||
<translatorcomment>Placeholder</translatorcomment>
|
|
||||||
<translation>Иван</translation>
|
<translation>Иван</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -166,7 +58,6 @@ To report bugs you can use:</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>A resource name like "Home" or "Work"</source>
|
<source>A resource name like "Home" or "Work"</source>
|
||||||
<translatorcomment>Tooltip</translatorcomment>
|
|
||||||
<translation>Имя этой программы для ваших контактов, может быть "Home" или "Phone"</translation>
|
<translation>Имя этой программы для ваших контактов, может быть "Home" или "Phone"</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -178,14 +69,6 @@ To report bugs you can use:</source>
|
|||||||
<source>Password storage</source>
|
<source>Password storage</source>
|
||||||
<translation>Хранение пароля</translation>
|
<translation>Хранение пароля</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Active</source>
|
|
||||||
<translation>Активен</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>enable</source>
|
|
||||||
<translation>включен</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Accounts</name>
|
<name>Accounts</name>
|
||||||
@ -215,139 +98,30 @@ To report bugs you can use:</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<translation type="vanished">Отключить</translation>
|
<translation>Отключить</translation>
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Deactivate</source>
|
|
||||||
<translation>Деактивировать</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Activate</source>
|
|
||||||
<translation>Активировать</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
|
||||||
<name>Application</name>
|
|
||||||
<message>
|
|
||||||
<source> from </source>
|
|
||||||
<translation> от </translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Attached file</source>
|
|
||||||
<translation>Прикрепленный файл</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Mark as Read</source>
|
|
||||||
<translation>Пометить прочитанным</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Open conversation</source>
|
|
||||||
<translation>Открыть окно беседы</translation>
|
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Conversation</name>
|
<name>Conversation</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Type your message here...</source>
|
<source>Type your message here...</source>
|
||||||
<translatorcomment>Placeholder</translatorcomment>
|
|
||||||
<translation>Введите сообщение...</translation>
|
<translation>Введите сообщение...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Chose a file to send</source>
|
<source>Chose a file to send</source>
|
||||||
<translation>Выберите файл для отправки</translation>
|
<translation>Выберите файл для отправки</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Drop files here to attach them to your message</source>
|
|
||||||
<translation>Бросьте файлы сюда для того что бы прикрепить их к сообщению</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></source>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></source>
|
||||||
<translation><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<translation></translation>
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
|
||||||
p, li { white-space: pre-wrap; }
|
|
||||||
</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;">
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></translation>
|
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Paste Image</source>
|
<source>Drop files here to attach them to your message</source>
|
||||||
<translation>Вставить изображение</translation>
|
<translation>Бросьте файлы сюда для того что бы прикрепить их к сообщению</translation>
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Try sending again</source>
|
|
||||||
<translation>Отправить снова</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Copy selected</source>
|
|
||||||
<translation>Скопировать выделенное</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Copy message</source>
|
|
||||||
<translation>Скопировать сообщение</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Open</source>
|
|
||||||
<translation>Открыть</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Show in folder</source>
|
|
||||||
<translation>Показать в проводнике</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Edit</source>
|
|
||||||
<translation>Редактировать</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Editing message...</source>
|
|
||||||
<translation>Сообщение редактируется...</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
|
||||||
<name>CredentialsPrompt</name>
|
|
||||||
<message>
|
|
||||||
<source>Authentication error: %1</source>
|
|
||||||
<translatorcomment>Window title</translatorcomment>
|
|
||||||
<translation>Ошибка аутентификации: %1</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Couldn't authenticate account %1: login or password is icorrect.
|
|
||||||
Would you like to check them and try again?</source>
|
|
||||||
<translation>Не получилось аутентифицировать
|
|
||||||
учетную запись %1:
|
|
||||||
имя пользователя или пароль введены неверно.
|
|
||||||
Желаете ли проверить их и
|
|
||||||
попробовать аутентифицироваться еще раз?</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Login</source>
|
|
||||||
<translation>Имя учетной записи</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Your account login (without @server.domain)</source>
|
|
||||||
<translatorcomment>Tooltip</translatorcomment>
|
|
||||||
<translation>Имя вашей учтетной записи (без @server.domain)</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Password</source>
|
|
||||||
<translation>Пароль</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Your password</source>
|
|
||||||
<translation>Ваш пароль</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
|
||||||
<name>DialogQueue</name>
|
|
||||||
<message>
|
|
||||||
<source>Input the password for account %1</source>
|
|
||||||
<translation>Введите пароль для учетной записи %1</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Password for account %1</source>
|
|
||||||
<translation>Пароль для учетной записи %1</translation>
|
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -596,14 +370,14 @@ Would you like to check them and try again?</source>
|
|||||||
<name>Message</name>
|
<name>Message</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Open</source>
|
<source>Open</source>
|
||||||
<translation type="vanished">Открыть</translation>
|
<translation>Открыть</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MessageLine</name>
|
<name>MessageLine</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Downloading...</source>
|
<source>Downloading...</source>
|
||||||
<translation type="vanished">Скачивается...</translation>
|
<translation>Скачивается...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download</source>
|
<source>Download</source>
|
||||||
@ -612,28 +386,28 @@ Would you like to check them and try again?</source>
|
|||||||
<message>
|
<message>
|
||||||
<source>Error uploading file: %1
|
<source>Error uploading file: %1
|
||||||
You can try again</source>
|
You can try again</source>
|
||||||
<translation type="vanished">Ошибка загрузки файла на сервер:
|
<translation>Ошибка загрузки файла на сервер:
|
||||||
%1
|
%1
|
||||||
Для того, что бы попробовать снова нажмите на кнопку</translation>
|
Для того, что бы попробовать снова нажмите на кнопку</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Upload</source>
|
<source>Upload</source>
|
||||||
<translation type="vanished">Загрузить</translation>
|
<translation>Загрузить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error downloading file: %1
|
<source>Error downloading file: %1
|
||||||
You can try again</source>
|
You can try again</source>
|
||||||
<translation type="vanished">Ошибка скачивания файла:
|
<translation>Ошибка скачивания файла:
|
||||||
%1
|
%1
|
||||||
Вы можете попробовать снова</translation>
|
Вы можете попробовать снова</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Uploading...</source>
|
<source>Uploading...</source>
|
||||||
<translation type="vanished">Загружается...</translation>
|
<translation>Загружается...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Push the button to download the file</source>
|
<source>Push the button to download the file</source>
|
||||||
<translation type="vanished">Нажмите на кнопку что бы загрузить файл</translation>
|
<translation>Нажмите на кнопку что бы загрузить файл</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -707,7 +481,7 @@ You can try again</source>
|
|||||||
<name>NewContact</name>
|
<name>NewContact</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Add new contact</source>
|
<source>Add new contact</source>
|
||||||
<translatorcomment>Window title</translatorcomment>
|
<translatorcomment>Заголовок окна</translatorcomment>
|
||||||
<translation>Добавление нового контакта</translation>
|
<translation>Добавление нового контакта</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -728,7 +502,7 @@ You can try again</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>name@server.dmn</source>
|
<source>name@server.dmn</source>
|
||||||
<translatorcomment>Placeholder</translatorcomment>
|
<translatorcomment>Placeholder поля ввода JID</translatorcomment>
|
||||||
<translation>name@server.dmn</translation>
|
<translation>name@server.dmn</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -744,64 +518,6 @@ You can try again</source>
|
|||||||
<translation>Иван Иванов</translation>
|
<translation>Иван Иванов</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
|
||||||
<name>PageAppearance</name>
|
|
||||||
<message>
|
|
||||||
<source>Theme</source>
|
|
||||||
<translation>Оформление</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Color scheme</source>
|
|
||||||
<translation>Цветовая схема</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>System</source>
|
|
||||||
<translation>Системная</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
|
||||||
<name>PageGeneral</name>
|
|
||||||
<message>
|
|
||||||
<source>Downloads path</source>
|
|
||||||
<translation>Папка для сохраненных файлов</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Browse</source>
|
|
||||||
<translation>Выбрать</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Select where downloads folder is going to be</source>
|
|
||||||
<translation>Выберете папку, в которую будут сохраняться файлы</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
|
||||||
<name>Settings</name>
|
|
||||||
<message>
|
|
||||||
<source>Preferences</source>
|
|
||||||
<translatorcomment>Window title</translatorcomment>
|
|
||||||
<translation>Настройки</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>General</source>
|
|
||||||
<translation>Общее</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Appearance</source>
|
|
||||||
<translation>Внешний вид</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Apply</source>
|
|
||||||
<translation>Применить</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Cancel</source>
|
|
||||||
<translation>Отменить</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Ok</source>
|
|
||||||
<translation>Готово</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
|
||||||
<context>
|
<context>
|
||||||
<name>Squawk</name>
|
<name>Squawk</name>
|
||||||
<message>
|
<message>
|
||||||
@ -814,7 +530,6 @@ You can try again</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Squawk</source>
|
<source>Squawk</source>
|
||||||
<translatorcomment>Menu bar entry</translatorcomment>
|
|
||||||
<translation>Squawk</translation>
|
<translation>Squawk</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -835,11 +550,11 @@ You can try again</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<translation type="vanished">Отключить</translation>
|
<translation>Отключить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Connect</source>
|
<source>Connect</source>
|
||||||
<translation type="vanished">Подключить</translation>
|
<translation>Подключить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>VCard</source>
|
<source>VCard</source>
|
||||||
@ -912,40 +627,20 @@ to be displayed as %1</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Attached file</source>
|
<source>Attached file</source>
|
||||||
<translation type="vanished">Прикрепленный файл</translation>
|
<translation>Прикрепленный файл</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Input the password for account %1</source>
|
<source>Input the password for account %1</source>
|
||||||
<translation type="vanished">Введите пароль для учетной записи %1</translation>
|
<translation>Введите пароль для учетной записи %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password for account %1</source>
|
<source>Password for account %1</source>
|
||||||
<translation type="vanished">Пароль для учетной записи %1</translation>
|
<translation>Пароль для учетной записи %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Please select a contact to start chatting</source>
|
<source>Please select a contact to start chatting</source>
|
||||||
<translation>Выберите контакт или группу что бы начать переписку</translation>
|
<translation>Выберите контакт или группу что бы начать переписку</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Help</source>
|
|
||||||
<translation>Помощь</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Preferences</source>
|
|
||||||
<translation>Настройки</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>About Squawk</source>
|
|
||||||
<translation>О Программе Squawk</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Deactivate</source>
|
|
||||||
<translation>Деактивировать</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Activate</source>
|
|
||||||
<translation>Активировать</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>VCard</name>
|
<name>VCard</name>
|
||||||
|
@ -1,9 +1,43 @@
|
|||||||
target_sources(squawk PRIVATE
|
cmake_minimum_required(VERSION 3.3)
|
||||||
squawk.cpp
|
project(squawkUI)
|
||||||
squawk.h
|
|
||||||
squawk.ui
|
# Instruct CMake to run moc automatically when needed.
|
||||||
)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
# Instruct CMake to create code from Qt designer ui files
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
# Find the QtWidgets library
|
||||||
|
find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Core)
|
||||||
|
find_package(Boost 1.36.0 REQUIRED)
|
||||||
|
if(Boost_FOUND)
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(models)
|
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(widgets)
|
add_subdirectory(widgets)
|
||||||
|
|
||||||
|
set(squawkUI_SRC
|
||||||
|
squawk.cpp
|
||||||
|
models/accounts.cpp
|
||||||
|
models/roster.cpp
|
||||||
|
models/item.cpp
|
||||||
|
models/account.cpp
|
||||||
|
models/contact.cpp
|
||||||
|
models/presence.cpp
|
||||||
|
models/group.cpp
|
||||||
|
models/room.cpp
|
||||||
|
models/abstractparticipant.cpp
|
||||||
|
models/participant.cpp
|
||||||
|
models/reference.cpp
|
||||||
|
models/messagefeed.cpp
|
||||||
|
models/element.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tell CMake to create the helloworld executable
|
||||||
|
add_library(squawkUI STATIC ${squawkUI_SRC})
|
||||||
|
|
||||||
|
# Use the Widgets module from Qt 5.
|
||||||
|
target_link_libraries(squawkUI squawkWidgets)
|
||||||
|
target_link_libraries(squawkUI squawkUIUtils)
|
||||||
|
target_link_libraries(squawkUI Qt5::Widgets)
|
||||||
|
target_link_libraries(squawkUI Qt5::DBus)
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
target_sources(squawk PRIVATE
|
|
||||||
abstractparticipant.cpp
|
|
||||||
abstractparticipant.h
|
|
||||||
account.cpp
|
|
||||||
account.h
|
|
||||||
accounts.cpp
|
|
||||||
accounts.h
|
|
||||||
contact.cpp
|
|
||||||
contact.h
|
|
||||||
element.cpp
|
|
||||||
element.h
|
|
||||||
group.cpp
|
|
||||||
group.h
|
|
||||||
item.cpp
|
|
||||||
item.h
|
|
||||||
participant.cpp
|
|
||||||
participant.h
|
|
||||||
presence.cpp
|
|
||||||
presence.h
|
|
||||||
reference.cpp
|
|
||||||
reference.h
|
|
||||||
room.cpp
|
|
||||||
room.h
|
|
||||||
roster.cpp
|
|
||||||
roster.h
|
|
||||||
)
|
|
@ -31,9 +31,7 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
|
|||||||
avatarPath(data.value("avatarPath").toString()),
|
avatarPath(data.value("avatarPath").toString()),
|
||||||
state(Shared::ConnectionState::disconnected),
|
state(Shared::ConnectionState::disconnected),
|
||||||
availability(Shared::Availability::offline),
|
availability(Shared::Availability::offline),
|
||||||
passwordType(Shared::AccountPassword::plain),
|
passwordType(Shared::AccountPassword::plain)
|
||||||
wasEverConnected(false),
|
|
||||||
active(false)
|
|
||||||
{
|
{
|
||||||
QMap<QString, QVariant>::const_iterator sItr = data.find("state");
|
QMap<QString, QVariant>::const_iterator sItr = data.find("state");
|
||||||
if (sItr != data.end()) {
|
if (sItr != data.end()) {
|
||||||
@ -47,10 +45,6 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
|
|||||||
if (pItr != data.end()) {
|
if (pItr != data.end()) {
|
||||||
setPasswordType(pItr.value().toUInt());
|
setPasswordType(pItr.value().toUInt());
|
||||||
}
|
}
|
||||||
QMap<QString, QVariant>::const_iterator acItr = data.find("active");
|
|
||||||
if (acItr != data.end()) {
|
|
||||||
setActive(acItr.value().toBool());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Account::~Account()
|
Models::Account::~Account()
|
||||||
@ -62,19 +56,8 @@ void Models::Account::setState(Shared::ConnectionState p_state)
|
|||||||
if (state != p_state) {
|
if (state != p_state) {
|
||||||
state = p_state;
|
state = p_state;
|
||||||
changed(2);
|
changed(2);
|
||||||
switch (state) {
|
if (state == Shared::ConnectionState::disconnected) {
|
||||||
case Shared::ConnectionState::disconnected:
|
toOfflineState();
|
||||||
toOfflineState();
|
|
||||||
break;
|
|
||||||
case Shared::ConnectionState::connected:
|
|
||||||
if (wasEverConnected) {
|
|
||||||
emit reconnected();
|
|
||||||
} else {
|
|
||||||
wasEverConnected = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,8 +164,6 @@ QVariant Models::Account::data(int column) const
|
|||||||
return avatarPath;
|
return avatarPath;
|
||||||
case 9:
|
case 9:
|
||||||
return Shared::Global::getName(passwordType);
|
return Shared::Global::getName(passwordType);
|
||||||
case 10:
|
|
||||||
return active;
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -190,7 +171,7 @@ QVariant Models::Account::data(int column) const
|
|||||||
|
|
||||||
int Models::Account::columnCount() const
|
int Models::Account::columnCount() const
|
||||||
{
|
{
|
||||||
return 11;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::Account::update(const QString& field, const QVariant& value)
|
void Models::Account::update(const QString& field, const QVariant& value)
|
||||||
@ -215,8 +196,6 @@ void Models::Account::update(const QString& field, const QVariant& value)
|
|||||||
setAvatarPath(value.toString());
|
setAvatarPath(value.toString());
|
||||||
} else if (field == "passwordType") {
|
} else if (field == "passwordType") {
|
||||||
setPasswordType(value.toUInt());
|
setPasswordType(value.toUInt());
|
||||||
} else if (field == "active") {
|
|
||||||
setActive(value.toBool());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,16 +269,3 @@ void Models::Account::setPasswordType(unsigned int pt)
|
|||||||
{
|
{
|
||||||
setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(pt));
|
setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(pt));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Models::Account::getActive() const
|
|
||||||
{
|
|
||||||
return active;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Account::setActive(bool p_active)
|
|
||||||
{
|
|
||||||
if (active != p_active) {
|
|
||||||
active = p_active;
|
|
||||||
changed(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -58,9 +58,6 @@ namespace Models {
|
|||||||
|
|
||||||
void setAvatarPath(const QString& path);
|
void setAvatarPath(const QString& path);
|
||||||
QString getAvatarPath() const;
|
QString getAvatarPath() const;
|
||||||
|
|
||||||
void setActive(bool active);
|
|
||||||
bool getActive() const;
|
|
||||||
|
|
||||||
void setAvailability(Shared::Availability p_avail);
|
void setAvailability(Shared::Availability p_avail);
|
||||||
void setAvailability(unsigned int p_avail);
|
void setAvailability(unsigned int p_avail);
|
||||||
@ -80,9 +77,6 @@ namespace Models {
|
|||||||
QString getBareJid() const;
|
QString getBareJid() const;
|
||||||
QString getFullJid() const;
|
QString getFullJid() const;
|
||||||
|
|
||||||
signals:
|
|
||||||
void reconnected();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString login;
|
QString login;
|
||||||
QString password;
|
QString password;
|
||||||
@ -93,8 +87,6 @@ namespace Models {
|
|||||||
Shared::ConnectionState state;
|
Shared::ConnectionState state;
|
||||||
Shared::Availability availability;
|
Shared::Availability availability;
|
||||||
Shared::AccountPassword passwordType;
|
Shared::AccountPassword passwordType;
|
||||||
bool wasEverConnected;
|
|
||||||
bool active;
|
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void toOfflineState() override;
|
void toOfflineState() override;
|
||||||
|
@ -48,10 +48,6 @@ QVariant Models::Accounts::data (const QModelIndex& index, int role) const
|
|||||||
answer = Shared::connectionStateIcon(accs[index.row()]->getState());
|
answer = Shared::connectionStateIcon(accs[index.row()]->getState());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Qt::ForegroundRole:
|
|
||||||
if (!accs[index.row()]->getActive()) {
|
|
||||||
answer = qApp->palette().brush(QPalette::Disabled, QPalette::Text);
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -101,10 +97,7 @@ void Models::Accounts::addAccount(Account* account)
|
|||||||
|
|
||||||
void Models::Accounts::onAccountChanged(Item* item, int row, int col)
|
void Models::Accounts::onAccountChanged(Item* item, int row, int col)
|
||||||
{
|
{
|
||||||
if (row < 0) {
|
if (row < accs.size()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (static_cast<std::deque<Models::Account*>::size_type>(row) < accs.size()) {
|
|
||||||
Account* acc = getAccount(row);
|
Account* acc = getAccount(row);
|
||||||
if (item != acc) {
|
if (item != acc) {
|
||||||
return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that
|
return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that
|
||||||
|
@ -155,16 +155,6 @@ void Models::Contact::removePresence(const QString& name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Presence * Models::Contact::getPresence(const QString& name)
|
|
||||||
{
|
|
||||||
QMap<QString, Presence*>::iterator itr = presences.find(name);
|
|
||||||
if (itr == presences.end()) {
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
return itr.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Contact::refresh()
|
void Models::Contact::refresh()
|
||||||
{
|
{
|
||||||
QDateTime lastActivity;
|
QDateTime lastActivity;
|
||||||
@ -250,9 +240,3 @@ QString Models::Contact::getDisplayedName() const
|
|||||||
return getContactName();
|
return getContactName();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::Contact::handleRecconnect()
|
|
||||||
{
|
|
||||||
if (getMessagesCount() > 0) {
|
|
||||||
feed->requestLatestMessages();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -51,14 +51,11 @@ public:
|
|||||||
|
|
||||||
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
|
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removePresence(const QString& name);
|
void removePresence(const QString& name);
|
||||||
Presence* getPresence(const QString& name);
|
|
||||||
|
|
||||||
QString getContactName() const;
|
QString getContactName() const;
|
||||||
QString getStatus() const;
|
QString getStatus() const;
|
||||||
QString getDisplayedName() const override;
|
QString getDisplayedName() const override;
|
||||||
|
|
||||||
void handleRecconnect(); //this is a special method Models::Roster calls when reconnect happens
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _removeChild(int index) override;
|
void _removeChild(int index) override;
|
||||||
void _appendChild(Models::Item * child) override;
|
void _appendChild(Models::Item * child) override;
|
||||||
|
@ -134,11 +134,6 @@ unsigned int Models::Element::getMessagesCount() const
|
|||||||
return feed->unreadMessagesCount();
|
return feed->unreadMessagesCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Models::Element::markMessageAsRead(const QString& id) const
|
|
||||||
{
|
|
||||||
return feed->markMessageAsRead(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::addMessage(const Shared::Message& data)
|
void Models::Element::addMessage(const Shared::Message& data)
|
||||||
{
|
{
|
||||||
feed->addMessage(data);
|
feed->addMessage(data);
|
||||||
@ -176,7 +171,6 @@ void Models::Element::fileError(const QString& messageId, const QString& error,
|
|||||||
|
|
||||||
void Models::Element::onFeedUnreadMessagesCountChanged()
|
void Models::Element::onFeedUnreadMessagesCountChanged()
|
||||||
{
|
{
|
||||||
emit unreadMessagesCountChanged();
|
|
||||||
if (type == contact) {
|
if (type == contact) {
|
||||||
changed(4);
|
changed(4);
|
||||||
} else if (type == room) {
|
} else if (type == room) {
|
||||||
|
@ -20,8 +20,7 @@
|
|||||||
#define ELEMENT_H
|
#define ELEMENT_H
|
||||||
|
|
||||||
#include "item.h"
|
#include "item.h"
|
||||||
|
#include "messagefeed.h"
|
||||||
#include "ui/widgets/messageline/messagefeed.h"
|
|
||||||
|
|
||||||
namespace Models {
|
namespace Models {
|
||||||
|
|
||||||
@ -42,7 +41,6 @@ public:
|
|||||||
void addMessage(const Shared::Message& data);
|
void addMessage(const Shared::Message& data);
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
unsigned int getMessagesCount() const;
|
unsigned int getMessagesCount() const;
|
||||||
bool markMessageAsRead(const QString& id) const;
|
|
||||||
void responseArchive(const std::list<Shared::Message> list, bool last);
|
void responseArchive(const std::list<Shared::Message> list, bool last);
|
||||||
bool isRoom() const;
|
bool isRoom() const;
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
void fileProgress(const QString& messageId, qreal value, bool up);
|
||||||
@ -53,7 +51,6 @@ signals:
|
|||||||
void requestArchive(const QString& before);
|
void requestArchive(const QString& before);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
void unreadMessagesCountChanged();
|
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -17,9 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "messagefeed.h"
|
#include "messagefeed.h"
|
||||||
|
#include "element.h"
|
||||||
#include <ui/models/element.h>
|
#include "room.h"
|
||||||
#include <ui/models/room.h>
|
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@ -46,8 +45,6 @@ Models::MessageFeed::MessageFeed(const Element* ri, QObject* parent):
|
|||||||
syncState(incomplete),
|
syncState(incomplete),
|
||||||
uploads(),
|
uploads(),
|
||||||
downloads(),
|
downloads(),
|
||||||
failedDownloads(),
|
|
||||||
failedUploads(),
|
|
||||||
unreadMessages(new std::set<QString>()),
|
unreadMessages(new std::set<QString>()),
|
||||||
observersAmount(0)
|
observersAmount(0)
|
||||||
{
|
{
|
||||||
@ -86,7 +83,7 @@ void Models::MessageFeed::addMessage(const Shared::Message& msg)
|
|||||||
emit newMessage(msg);
|
emit newMessage(msg);
|
||||||
|
|
||||||
if (observersAmount == 0 && !msg.getForwarded()) { //not to notify when the message is delivered by the carbon copy
|
if (observersAmount == 0 && !msg.getForwarded()) { //not to notify when the message is delivered by the carbon copy
|
||||||
unreadMessages->insert(id); //cuz it could be my own one or the one I read on another device
|
unreadMessages->insert(msg.getId()); //cuz it could be my own one or the one I read on another device
|
||||||
emit unreadMessagesCountChanged();
|
emit unreadMessagesCountChanged();
|
||||||
emit unnoticedMessage(msg);
|
emit unnoticedMessage(msg);
|
||||||
}
|
}
|
||||||
@ -145,30 +142,12 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap<QString, Q
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err::const_iterator eitr = failedDownloads.find(id);
|
|
||||||
if (eitr != failedDownloads.end()) {
|
|
||||||
failedDownloads.erase(eitr);
|
|
||||||
changeRoles.insert(MessageRoles::Attach);
|
|
||||||
} else {
|
|
||||||
eitr = failedUploads.find(id);
|
|
||||||
if (eitr != failedUploads.end()) {
|
|
||||||
failedUploads.erase(eitr);
|
|
||||||
changeRoles.insert(MessageRoles::Attach);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> cr;
|
QVector<int> cr;
|
||||||
for (MessageRoles role : changeRoles) {
|
for (MessageRoles role : changeRoles) {
|
||||||
cr.push_back(role);
|
cr.push_back(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(index, index, cr);
|
emit dataChanged(index, index, cr);
|
||||||
|
|
||||||
if (observersAmount == 0 && !msg->getForwarded() && changeRoles.count(MessageRoles::Text) > 0) {
|
|
||||||
unreadMessages->insert(id);
|
|
||||||
emit unreadMessagesCountChanged();
|
|
||||||
emit unnoticedMessage(*msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,20 +209,8 @@ std::set<Models::MessageFeed::MessageRoles> Models::MessageFeed::detectChanges(c
|
|||||||
|
|
||||||
void Models::MessageFeed::removeMessage(const QString& id)
|
void Models::MessageFeed::removeMessage(const QString& id)
|
||||||
{
|
{
|
||||||
//todo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Message Models::MessageFeed::getMessage(const QString& id)
|
|
||||||
{
|
|
||||||
StorageById::iterator itr = indexById.find(id);
|
|
||||||
if (itr == indexById.end()) {
|
|
||||||
throw NotFound(id.toStdString(), rosterItem->getJid().toStdString(), rosterItem->getAccountName().toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return **itr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
||||||
{
|
{
|
||||||
int i = index.row();
|
int i = index.row();
|
||||||
@ -280,7 +247,7 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
|||||||
answer = static_cast<unsigned int>(msg->getState());
|
answer = static_cast<unsigned int>(msg->getState());
|
||||||
break;
|
break;
|
||||||
case Correction:
|
case Correction:
|
||||||
answer.setValue(fillCorrection(*msg));;
|
answer = msg->getEdited();
|
||||||
break;
|
break;
|
||||||
case SentByMe:
|
case SentByMe:
|
||||||
answer = sentByMe(*msg);
|
answer = sentByMe(*msg);
|
||||||
@ -318,13 +285,18 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
|||||||
case Bulk: {
|
case Bulk: {
|
||||||
FeedItem item;
|
FeedItem item;
|
||||||
item.id = msg->getId();
|
item.id = msg->getId();
|
||||||
markMessageAsRead(item.id);
|
|
||||||
|
std::set<QString>::const_iterator umi = unreadMessages->find(item.id);
|
||||||
|
if (umi != unreadMessages->end()) {
|
||||||
|
unreadMessages->erase(umi);
|
||||||
|
emit unreadMessagesCount();
|
||||||
|
}
|
||||||
|
|
||||||
item.sentByMe = sentByMe(*msg);
|
item.sentByMe = sentByMe(*msg);
|
||||||
item.date = msg->getTime();
|
item.date = msg->getTime();
|
||||||
item.state = msg->getState();
|
item.state = msg->getState();
|
||||||
item.error = msg->getErrorText();
|
item.error = msg->getErrorText();
|
||||||
item.correction = fillCorrection(*msg);
|
item.correction = msg->getEdited();
|
||||||
|
|
||||||
QString body = msg->getBody();
|
QString body = msg->getBody();
|
||||||
if (body != msg->getOutOfBandUrl()) {
|
if (body != msg->getOutOfBandUrl()) {
|
||||||
@ -368,17 +340,6 @@ int Models::MessageFeed::rowCount(const QModelIndex& parent) const
|
|||||||
return storage.size();
|
return storage.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Models::MessageFeed::markMessageAsRead(const QString& id) const
|
|
||||||
{
|
|
||||||
std::set<QString>::const_iterator umi = unreadMessages->find(id);
|
|
||||||
if (umi != unreadMessages->end()) {
|
|
||||||
unreadMessages->erase(umi);
|
|
||||||
emit unreadMessagesCountChanged();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
||||||
{
|
{
|
||||||
return unreadMessages->size();
|
return unreadMessages->size();
|
||||||
@ -394,6 +355,7 @@ void Models::MessageFeed::fetchMore(const QModelIndex& parent)
|
|||||||
if (syncState == incomplete) {
|
if (syncState == incomplete) {
|
||||||
syncState = syncing;
|
syncState = syncing;
|
||||||
emit syncStateChange(syncState);
|
emit syncStateChange(syncState);
|
||||||
|
emit requestStateChange(true);
|
||||||
|
|
||||||
if (storage.size() == 0) {
|
if (storage.size() == 0) {
|
||||||
emit requestArchive("");
|
emit requestArchive("");
|
||||||
@ -421,6 +383,7 @@ void Models::MessageFeed::responseArchive(const std::list<Shared::Message> list,
|
|||||||
syncState = incomplete;
|
syncState = incomplete;
|
||||||
}
|
}
|
||||||
emit syncStateChange(syncState);
|
emit syncStateChange(syncState);
|
||||||
|
emit requestStateChange(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,7 +421,6 @@ bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const
|
|||||||
Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const
|
Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const
|
||||||
{
|
{
|
||||||
::Models::Attachment att;
|
::Models::Attachment att;
|
||||||
QString id = msg.getId();
|
|
||||||
|
|
||||||
att.localPath = msg.getAttachPath();
|
att.localPath = msg.getAttachPath();
|
||||||
att.remotePath = msg.getOutOfBandUrl();
|
att.remotePath = msg.getOutOfBandUrl();
|
||||||
@ -467,34 +429,22 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c
|
|||||||
if (att.localPath.size() == 0) {
|
if (att.localPath.size() == 0) {
|
||||||
att.state = none;
|
att.state = none;
|
||||||
} else {
|
} else {
|
||||||
Err::const_iterator eitr = failedUploads.find(id);
|
Progress::const_iterator itr = uploads.find(msg.getId());
|
||||||
if (eitr != failedUploads.end()) {
|
if (itr == uploads.end()) {
|
||||||
att.state = errorUpload;
|
att.state = local;
|
||||||
att.error = eitr->second;
|
|
||||||
} else {
|
} else {
|
||||||
Progress::const_iterator itr = uploads.find(id);
|
att.state = uploading;
|
||||||
if (itr == uploads.end()) {
|
att.progress = itr->second;
|
||||||
att.state = local;
|
|
||||||
} else {
|
|
||||||
att.state = uploading;
|
|
||||||
att.progress = itr->second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (att.localPath.size() == 0) {
|
if (att.localPath.size() == 0) {
|
||||||
Err::const_iterator eitr = failedDownloads.find(id);
|
Progress::const_iterator itr = downloads.find(msg.getId());
|
||||||
if (eitr != failedDownloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
att.state = errorDownload;
|
att.state = remote;
|
||||||
att.error = eitr->second;
|
|
||||||
} else {
|
} else {
|
||||||
Progress::const_iterator itr = downloads.find(id);
|
att.state = downloading;
|
||||||
if (itr == downloads.end()) {
|
att.progress = itr->second;
|
||||||
att.state = remote;
|
|
||||||
} else {
|
|
||||||
att.state = downloading;
|
|
||||||
att.progress = itr->second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
att.state = ready;
|
att.state = ready;
|
||||||
@ -504,29 +454,14 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c
|
|||||||
return att;
|
return att;
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Edition Models::MessageFeed::fillCorrection(const Shared::Message& msg) const
|
|
||||||
{
|
|
||||||
::Models::Edition ed({msg.getEdited(), msg.getOriginalBody(), msg.getLastModified()});
|
|
||||||
|
|
||||||
return ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Models::MessageFeed::downloadAttachment(const QString& messageId)
|
void Models::MessageFeed::downloadAttachment(const QString& messageId)
|
||||||
{
|
{
|
||||||
bool notify = false;
|
|
||||||
Err::const_iterator eitr = failedDownloads.find(messageId);
|
|
||||||
if (eitr != failedDownloads.end()) {
|
|
||||||
failedDownloads.erase(eitr);
|
|
||||||
notify = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex ind = modelIndexById(messageId);
|
QModelIndex ind = modelIndexById(messageId);
|
||||||
if (ind.isValid()) {
|
if (ind.isValid()) {
|
||||||
std::pair<Progress::iterator, bool> progressPair = downloads.insert(std::make_pair(messageId, 0));
|
std::pair<Progress::iterator, bool> progressPair = downloads.insert(std::make_pair(messageId, 0));
|
||||||
if (progressPair.second) { //Only to take action if we weren't already downloading it
|
if (progressPair.second) { //Only to take action if we weren't already downloading it
|
||||||
Shared::Message* msg = static_cast<Shared::Message*>(ind.internalPointer());
|
Shared::Message* msg = static_cast<Shared::Message*>(ind.internalPointer());
|
||||||
notify = true;
|
emit dataChanged(ind, ind, {MessageRoles::Attach});
|
||||||
emit fileDownloadRequest(msg->getOutOfBandUrl());
|
emit fileDownloadRequest(msg->getOutOfBandUrl());
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Attachment download for message with id" << messageId << "is already in progress, skipping";
|
qDebug() << "Attachment download for message with id" << messageId << "is already in progress, skipping";
|
||||||
@ -534,55 +469,32 @@ void Models::MessageFeed::downloadAttachment(const QString& messageId)
|
|||||||
} else {
|
} else {
|
||||||
qDebug() << "An attempt to download an attachment for the message that doesn't exist. ID:" << messageId;
|
qDebug() << "An attempt to download an attachment for the message that doesn't exist. ID:" << messageId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (notify) {
|
|
||||||
emit dataChanged(ind, ind, {MessageRoles::Attach});
|
void Models::MessageFeed::uploadAttachment(const QString& messageId)
|
||||||
}
|
{
|
||||||
|
qDebug() << "request to upload attachment of the message" << messageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Models::MessageFeed::registerUpload(const QString& messageId)
|
bool Models::MessageFeed::registerUpload(const QString& messageId)
|
||||||
{
|
{
|
||||||
bool success = uploads.insert(std::make_pair(messageId, 0)).second;
|
return uploads.insert(std::make_pair(messageId, 0)).second;
|
||||||
|
|
||||||
QVector<int> roles({});
|
|
||||||
Err::const_iterator eitr = failedUploads.find(messageId);
|
|
||||||
if (eitr != failedUploads.end()) {
|
|
||||||
failedUploads.erase(eitr);
|
|
||||||
roles.push_back(MessageRoles::Attach);
|
|
||||||
} else if (success) {
|
|
||||||
roles.push_back(MessageRoles::Attach);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex ind = modelIndexById(messageId);
|
|
||||||
emit dataChanged(ind, ind, roles);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up)
|
void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up)
|
||||||
{
|
{
|
||||||
Progress* pr = 0;
|
Progress* pr = 0;
|
||||||
Err* err = 0;
|
|
||||||
if (up) {
|
if (up) {
|
||||||
pr = &uploads;
|
pr = &uploads;
|
||||||
err = &failedUploads;
|
|
||||||
} else {
|
} else {
|
||||||
pr = &downloads;
|
pr = &downloads;
|
||||||
err = &failedDownloads;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> roles({});
|
|
||||||
Err::const_iterator eitr = err->find(messageId);
|
|
||||||
if (eitr != err->end() && value != 1) { //like I want to clear this state when the download is started anew
|
|
||||||
err->erase(eitr);
|
|
||||||
roles.push_back(MessageRoles::Attach);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Progress::iterator itr = pr->find(messageId);
|
Progress::iterator itr = pr->find(messageId);
|
||||||
if (itr != pr->end()) {
|
if (itr != pr->end()) {
|
||||||
itr->second = value;
|
itr->second = value;
|
||||||
QModelIndex ind = modelIndexById(messageId);
|
QModelIndex ind = modelIndexById(messageId);
|
||||||
emit dataChanged(ind, ind, roles);
|
emit dataChanged(ind, ind); //the type of the attach didn't change, so, there is no need to relayout, there is no role in event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,29 +505,7 @@ void Models::MessageFeed::fileComplete(const QString& messageId, bool up)
|
|||||||
|
|
||||||
void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up)
|
void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up)
|
||||||
{
|
{
|
||||||
Err* failed;
|
//TODO
|
||||||
Progress* loads;
|
|
||||||
if (up) {
|
|
||||||
failed = &failedUploads;
|
|
||||||
loads = &uploads;
|
|
||||||
} else {
|
|
||||||
failed = &failedDownloads;
|
|
||||||
loads = &downloads;
|
|
||||||
}
|
|
||||||
|
|
||||||
Progress::iterator pitr = loads->find(messageId);
|
|
||||||
if (pitr != loads->end()) {
|
|
||||||
loads->erase(pitr);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Err::iterator, bool> pair = failed->insert(std::make_pair(messageId, error));
|
|
||||||
if (!pair.second) {
|
|
||||||
pair.first->second = error;
|
|
||||||
}
|
|
||||||
QModelIndex ind = modelIndexById(messageId);
|
|
||||||
if (ind.isValid()) {
|
|
||||||
emit dataChanged(ind, ind, {MessageRoles::Attach});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::MessageFeed::incrementObservers()
|
void Models::MessageFeed::incrementObservers()
|
||||||
@ -642,22 +532,20 @@ QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const
|
|||||||
|
|
||||||
QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const
|
QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const
|
||||||
{
|
{
|
||||||
if (indexByTime.size() > 0) {
|
StorageByTime::const_iterator tItr = indexByTime.upper_bound(time);
|
||||||
StorageByTime::const_iterator tItr = indexByTime.lower_bound(time);
|
StorageByTime::const_iterator tBeg = indexByTime.begin();
|
||||||
StorageByTime::const_iterator tEnd = indexByTime.upper_bound(time);
|
bool found = false;
|
||||||
bool found = false;
|
while (tItr != tBeg) {
|
||||||
while (tItr != tEnd) {
|
if (id == (*tItr)->getId()) {
|
||||||
if (id == (*tItr)->getId()) {
|
found = true;
|
||||||
found = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
++tItr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
int position = indexByTime.rank(tItr);
|
|
||||||
return createIndex(position, 0, *tItr);
|
|
||||||
}
|
}
|
||||||
|
--tItr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found || id == (*tItr)->getId()) {
|
||||||
|
int position = indexByTime.rank(tItr);
|
||||||
|
return createIndex(position, 0, *tItr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
@ -675,7 +563,7 @@ void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId)
|
|||||||
|
|
||||||
emit localPathInvalid(msg->getAttachPath());
|
emit localPathInvalid(msg->getAttachPath());
|
||||||
|
|
||||||
//gonna change the message in current model right away, to prevent spam on each attempt to draw element
|
//gonna change the message in current model right away, to prevent spam on each attemt to draw element
|
||||||
QModelIndex index = modelIndexByTime(messageId, msg->getTime());
|
QModelIndex index = modelIndexByTime(messageId, msg->getTime());
|
||||||
msg->setAttachPath("");
|
msg->setAttachPath("");
|
||||||
|
|
||||||
@ -686,13 +574,3 @@ Models::MessageFeed::SyncState Models::MessageFeed::getSyncState() const
|
|||||||
{
|
{
|
||||||
return syncState;
|
return syncState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::MessageFeed::requestLatestMessages()
|
|
||||||
{
|
|
||||||
if (syncState != syncing) {
|
|
||||||
syncState = syncing;
|
|
||||||
emit syncStateChange(syncState);
|
|
||||||
|
|
||||||
emit requestArchive("");
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,13 +32,11 @@
|
|||||||
|
|
||||||
#include <shared/message.h>
|
#include <shared/message.h>
|
||||||
#include <shared/icons.h>
|
#include <shared/icons.h>
|
||||||
#include <shared/exception.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Models {
|
namespace Models {
|
||||||
class Element;
|
class Element;
|
||||||
struct Attachment;
|
struct Attachment;
|
||||||
struct Edition;
|
|
||||||
|
|
||||||
class MessageFeed : public QAbstractListModel
|
class MessageFeed : public QAbstractListModel
|
||||||
{
|
{
|
||||||
@ -56,9 +54,6 @@ public:
|
|||||||
void addMessage(const Shared::Message& msg);
|
void addMessage(const Shared::Message& msg);
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void removeMessage(const QString& id);
|
void removeMessage(const QString& id);
|
||||||
Shared::Message getMessage(const QString& id);
|
|
||||||
QModelIndex modelIndexById(const QString& id) const;
|
|
||||||
QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const;
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
@ -70,11 +65,11 @@ public:
|
|||||||
|
|
||||||
void responseArchive(const std::list<Shared::Message> list, bool last);
|
void responseArchive(const std::list<Shared::Message> list, bool last);
|
||||||
void downloadAttachment(const QString& messageId);
|
void downloadAttachment(const QString& messageId);
|
||||||
|
void uploadAttachment(const QString& messageId);
|
||||||
bool registerUpload(const QString& messageId);
|
bool registerUpload(const QString& messageId);
|
||||||
void reportLocalPathInvalid(const QString& messageId);
|
void reportLocalPathInvalid(const QString& messageId);
|
||||||
|
|
||||||
unsigned int unreadMessagesCount() const;
|
unsigned int unreadMessagesCount() const;
|
||||||
bool markMessageAsRead(const QString& id) const;
|
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
void fileProgress(const QString& messageId, qreal value, bool up);
|
||||||
void fileError(const QString& messageId, const QString& error, bool up);
|
void fileError(const QString& messageId, const QString& error, bool up);
|
||||||
void fileComplete(const QString& messageId, bool up);
|
void fileComplete(const QString& messageId, bool up);
|
||||||
@ -83,12 +78,11 @@ public:
|
|||||||
void decrementObservers();
|
void decrementObservers();
|
||||||
SyncState getSyncState() const;
|
SyncState getSyncState() const;
|
||||||
|
|
||||||
void requestLatestMessages(); //this method is used by Models::Contact to request latest messages after reconnection
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void requestArchive(const QString& before);
|
void requestArchive(const QString& before);
|
||||||
|
void requestStateChange(bool requesting);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
void unreadMessagesCountChanged() const;
|
void unreadMessagesCountChanged();
|
||||||
void newMessage(const Shared::Message& msg);
|
void newMessage(const Shared::Message& msg);
|
||||||
void unnoticedMessage(const Shared::Message& msg);
|
void unnoticedMessage(const Shared::Message& msg);
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
@ -108,26 +102,12 @@ public:
|
|||||||
Error,
|
Error,
|
||||||
Bulk
|
Bulk
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotFound:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NotFound(const std::string& k, const std::string& j, const std::string& acc):Exception(), key(k), jid(j), account(acc){}
|
|
||||||
|
|
||||||
std::string getMessage() const {
|
|
||||||
return "Message with id " + key + " wasn't found in messageFeed " + account + " of the chat with " + jid;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string key;
|
|
||||||
std::string jid;
|
|
||||||
std::string account;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool sentByMe(const Shared::Message& msg) const;
|
bool sentByMe(const Shared::Message& msg) const;
|
||||||
Attachment fillAttach(const Shared::Message& msg) const;
|
Attachment fillAttach(const Shared::Message& msg) const;
|
||||||
Edition fillCorrection(const Shared::Message& msg) const;
|
QModelIndex modelIndexById(const QString& id) const;
|
||||||
|
QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const;
|
||||||
std::set<MessageRoles> detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const;
|
std::set<MessageRoles> detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -168,16 +148,12 @@ private:
|
|||||||
SyncState syncState;
|
SyncState syncState;
|
||||||
|
|
||||||
typedef std::map<QString, qreal> Progress;
|
typedef std::map<QString, qreal> Progress;
|
||||||
typedef std::map<QString, QString> Err;
|
|
||||||
Progress uploads;
|
Progress uploads;
|
||||||
Progress downloads;
|
Progress downloads;
|
||||||
Err failedDownloads;
|
|
||||||
Err failedUploads;
|
|
||||||
|
|
||||||
std::set<QString>* unreadMessages;
|
std::set<QString>* unreadMessages;
|
||||||
uint16_t observersAmount;
|
uint16_t observersAmount;
|
||||||
|
|
||||||
|
|
||||||
static const QHash<int, QByteArray> roles;
|
static const QHash<int, QByteArray> roles;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,13 +173,6 @@ struct Attachment {
|
|||||||
qreal progress;
|
qreal progress;
|
||||||
QString localPath;
|
QString localPath;
|
||||||
QString remotePath;
|
QString remotePath;
|
||||||
QString error;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Edition {
|
|
||||||
bool corrected;
|
|
||||||
QString original;
|
|
||||||
QDateTime lastCorrection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FeedItem {
|
struct FeedItem {
|
||||||
@ -213,7 +182,7 @@ struct FeedItem {
|
|||||||
QString avatar;
|
QString avatar;
|
||||||
QString error;
|
QString error;
|
||||||
bool sentByMe;
|
bool sentByMe;
|
||||||
Edition correction;
|
bool correction;
|
||||||
QDateTime date;
|
QDateTime date;
|
||||||
Shared::Message::State state;
|
Shared::Message::State state;
|
||||||
Attachment attach;
|
Attachment attach;
|
||||||
@ -221,7 +190,6 @@ struct FeedItem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Models::Attachment);
|
Q_DECLARE_METATYPE(Models::Attachment);
|
||||||
Q_DECLARE_METATYPE(Models::Edition);
|
|
||||||
Q_DECLARE_METATYPE(Models::FeedItem);
|
Q_DECLARE_METATYPE(Models::FeedItem);
|
||||||
|
|
||||||
#endif // MESSAGEFEED_H
|
#endif // MESSAGEFEED_H
|
@ -104,8 +104,6 @@ void Models::Reference::onChildChanged(Models::Item* item, int row, int col)
|
|||||||
{
|
{
|
||||||
if (item == original) {
|
if (item == original) {
|
||||||
emit childChanged(this, row, col);
|
emit childChanged(this, row, col);
|
||||||
} else {
|
|
||||||
emit childChanged(item, row, col);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,16 +264,6 @@ void Models::Room::removeParticipant(const QString& p_name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Participant * Models::Room::getParticipant(const QString& p_name)
|
|
||||||
{
|
|
||||||
std::map<QString, Participant*>::const_iterator itr = participants.find(p_name);
|
|
||||||
if (itr == participants.end()) {
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
return itr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Room::handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data)
|
void Models::Room::handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Participant* part = itr->second;
|
Participant* part = itr->second;
|
||||||
|
@ -58,7 +58,6 @@ public:
|
|||||||
void addParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void addParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeParticipant(const QString& name);
|
void removeParticipant(const QString& name);
|
||||||
Participant* getParticipant(const QString& name);
|
|
||||||
|
|
||||||
void toOfflineState() override;
|
void toOfflineState() override;
|
||||||
QString getDisplayedName() const override;
|
QString getDisplayedName() const override;
|
||||||
|
@ -48,7 +48,6 @@ Models::Roster::~Roster()
|
|||||||
void Models::Roster::addAccount(const QMap<QString, QVariant>& data)
|
void Models::Roster::addAccount(const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Account* acc = new Account(data);
|
Account* acc = new Account(data);
|
||||||
connect(acc, &Account::reconnected, this, &Roster::onAccountReconnected);
|
|
||||||
root->appendChild(acc);
|
root->appendChild(acc);
|
||||||
accounts.insert(std::make_pair(acc->getName(), acc));
|
accounts.insert(std::make_pair(acc->getName(), acc));
|
||||||
accountsModel->addAccount(acc);
|
accountsModel->addAccount(acc);
|
||||||
@ -276,18 +275,6 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Qt::ForegroundRole:
|
|
||||||
switch (item->type) {
|
|
||||||
case Item::account: {
|
|
||||||
Account* acc = static_cast<Account*>(item);
|
|
||||||
if (!acc->getActive()) {
|
|
||||||
result = qApp->palette().brush(QPalette::Disabled, QPalette::Text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -463,7 +450,6 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
|||||||
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
||||||
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
||||||
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
||||||
connect(contact, &Contact::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
|
|
||||||
contacts.insert(std::make_pair(id, contact));
|
contacts.insert(std::make_pair(id, contact));
|
||||||
} else {
|
} else {
|
||||||
contact = itr->second;
|
contact = itr->second;
|
||||||
@ -549,8 +535,8 @@ void Models::Roster::removeGroup(const QString& account, const QString& name)
|
|||||||
|
|
||||||
void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement(ElId(account, jid));
|
Element* el = getElement({account, jid});
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
el->update(itr.key(), itr.value());
|
el->update(itr.key(), itr.value());
|
||||||
}
|
}
|
||||||
@ -559,11 +545,9 @@ void Models::Roster::changeContact(const QString& account, const QString& jid, c
|
|||||||
|
|
||||||
void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement(ElId(account, jid));
|
Element* el = getElement({account, jid});
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
el->changeMessage(id, data);
|
el->changeMessage(id, data);
|
||||||
} else {
|
|
||||||
qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,8 +691,8 @@ void Models::Roster::removePresence(const QString& account, const QString& jid,
|
|||||||
|
|
||||||
void Models::Roster::addMessage(const QString& account, const Shared::Message& data)
|
void Models::Roster::addMessage(const QString& account, const Shared::Message& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement(ElId(account, data.getPenPalJid()));
|
Element* el = getElement({account, data.getPenPalJid()});
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
el->addMessage(data);
|
el->addMessage(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,11 +744,10 @@ void Models::Roster::removeAccount(const QString& account)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(acc, &Account::reconnected, this, &Roster::onAccountReconnected);
|
|
||||||
acc->deleteLater();
|
acc->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Roster::getContactName(const QString& account, const QString& jid) const
|
QString Models::Roster::getContactName(const QString& account, const QString& jid)
|
||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
@ -802,11 +785,10 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM
|
|||||||
}
|
}
|
||||||
|
|
||||||
Room* room = new Room(acc, jid, data);
|
Room* room = new Room(acc, jid, data);
|
||||||
connect(room, &Room::requestArchive, this, &Roster::onElementRequestArchive);
|
connect(room, &Contact::requestArchive, this, &Roster::onElementRequestArchive);
|
||||||
connect(room, &Room::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
connect(room, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
||||||
connect(room, &Room::unnoticedMessage, this, &Roster::unnoticedMessage);
|
connect(room, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
||||||
connect(room, &Room::localPathInvalid, this, &Roster::localPathInvalid);
|
connect(room, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
||||||
connect(room, &Room::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
|
|
||||||
rooms.insert(std::make_pair(id, room));
|
rooms.insert(std::make_pair(id, room));
|
||||||
acc->appendChild(room);
|
acc->appendChild(room);
|
||||||
}
|
}
|
||||||
@ -909,7 +891,7 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const
|
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource)
|
||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
@ -929,36 +911,9 @@ QString Models::Roster::getContactIconPath(const QString& account, const QString
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Account * Models::Roster::getAccount(const QString& name) {
|
Models::Account * Models::Roster::getAccount(const QString& name)
|
||||||
return const_cast<Models::Account*>(getAccountConst(name));}
|
|
||||||
|
|
||||||
const Models::Account * Models::Roster::getAccountConst(const QString& name) const {
|
|
||||||
return accounts.at(name);}
|
|
||||||
|
|
||||||
const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const
|
|
||||||
{
|
{
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
return accounts.find(name)->second;
|
||||||
|
|
||||||
if (cItr != contacts.end()) {
|
|
||||||
return cItr->second;
|
|
||||||
} else {
|
|
||||||
std::map<ElId, Room*>::const_iterator rItr = rooms.find(id);
|
|
||||||
if (rItr != rooms.end()) {
|
|
||||||
return rItr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId)
|
|
||||||
{
|
|
||||||
const Element* el = getElementConst(elementId);
|
|
||||||
if (el != nullptr) {
|
|
||||||
return el->markMessageAsRead(messageId);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex Models::Roster::getAccountIndex(const QString& name)
|
QModelIndex Models::Roster::getAccountIndex(const QString& name)
|
||||||
@ -977,7 +932,7 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
if (itr == accounts.end()) {
|
if (itr == accounts.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
} else {
|
} else {
|
||||||
std::map<ElId, Group*>::const_iterator gItr = groups.find(ElId(account, name));
|
std::map<ElId, Group*>::const_iterator gItr = groups.find({account, name});
|
||||||
if (gItr == groups.end()) {
|
if (gItr == groups.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
} else {
|
} else {
|
||||||
@ -987,48 +942,6 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource)
|
|
||||||
{
|
|
||||||
std::map<QString, Account*>::const_iterator itr = accounts.find(account);
|
|
||||||
if (itr == accounts.end()) {
|
|
||||||
return QModelIndex();
|
|
||||||
} else {
|
|
||||||
Account* acc = itr->second;
|
|
||||||
QModelIndex accIndex = index(acc->row(), 0, QModelIndex());
|
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(ElId(account, jid));
|
|
||||||
if (cItr != contacts.end()) {
|
|
||||||
QModelIndex contactIndex = index(acc->getContact(jid), 0, accIndex);
|
|
||||||
if (resource.size() == 0) {
|
|
||||||
return contactIndex;
|
|
||||||
} else {
|
|
||||||
Presence* pres = cItr->second->getPresence(resource);
|
|
||||||
if (pres != nullptr) {
|
|
||||||
return index(pres->row(), 0, contactIndex);
|
|
||||||
} else {
|
|
||||||
return contactIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::map<ElId, Room*>::const_iterator rItr = rooms.find(ElId(account, jid));
|
|
||||||
if (rItr != rooms.end()) {
|
|
||||||
QModelIndex roomIndex = index(rItr->second->row(), 0, accIndex);
|
|
||||||
if (resource.size() == 0) {
|
|
||||||
return roomIndex;
|
|
||||||
} else {
|
|
||||||
Participant* part = rItr->second->getParticipant(resource);
|
|
||||||
if (part != nullptr) {
|
|
||||||
return index(part->row(), 0, roomIndex);
|
|
||||||
} else {
|
|
||||||
return roomIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Roster::onElementRequestArchive(const QString& before)
|
void Models::Roster::onElementRequestArchive(const QString& before)
|
||||||
{
|
{
|
||||||
Element* el = static_cast<Element*>(sender());
|
Element* el = static_cast<Element*>(sender());
|
||||||
@ -1039,7 +952,7 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid,
|
|||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
Element* el = getElement(id);
|
Element* el = getElement(id);
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
el->responseArchive(list, last);
|
el->responseArchive(list, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,8 +960,8 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid,
|
|||||||
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement(ElId(info.account, info.jid));
|
Element* el = getElement({info.account, info.jid});
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
el->fileProgress(info.messageId, value, up);
|
el->fileProgress(info.messageId, value, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1057,8 +970,8 @@ void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qr
|
|||||||
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement(ElId(info.account, info.jid));
|
Element* el = getElement({info.account, info.jid});
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
el->fileComplete(info.messageId, up);
|
el->fileComplete(info.messageId, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1067,8 +980,8 @@ void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bo
|
|||||||
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement(ElId(info.account, info.jid));
|
Element* el = getElement({info.account, info.jid});
|
||||||
if (el != nullptr) {
|
if (el != NULL) {
|
||||||
el->fileError(info.messageId, err, up);
|
el->fileError(info.messageId, err, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1076,40 +989,17 @@ void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const
|
|||||||
|
|
||||||
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
||||||
{
|
{
|
||||||
return const_cast<Models::Element*>(getElementConst(id));
|
std::map<ElId, Contact*>::iterator cItr = contacts.find(id);
|
||||||
}
|
|
||||||
|
|
||||||
Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const
|
|
||||||
{
|
|
||||||
const Models::Element* el = getElementConst(id);
|
|
||||||
if (el == nullptr) {
|
|
||||||
return Item::root;
|
|
||||||
}
|
|
||||||
|
|
||||||
return el->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Models::Roster::onAccountReconnected()
|
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
|
||||||
|
|
||||||
QString accName = acc->getName();
|
if (cItr != contacts.end()) {
|
||||||
for (const std::pair<const ElId, Contact*>& pair : contacts) {
|
return cItr->second;
|
||||||
if (pair.first.account == accName) {
|
} else {
|
||||||
pair.second->handleRecconnect();
|
std::map<ElId, Room*>::iterator rItr = rooms.find(id);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
return rItr->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::Roster::recalculateUnreadMessages()
|
|
||||||
{
|
|
||||||
int count(0);
|
|
||||||
for (const std::pair<const ElId, Contact*>& pair : contacts) {
|
|
||||||
count += pair.second->getMessagesCount();
|
|
||||||
}
|
|
||||||
for (const std::pair<const ElId, Room*>& pair : rooms) {
|
|
||||||
count += pair.second->getMessagesCount();
|
|
||||||
}
|
|
||||||
emit unreadMessagesCountChanged(count);
|
|
||||||
}
|
|
||||||
|
@ -46,7 +46,6 @@ public:
|
|||||||
Roster(QObject* parent = 0);
|
Roster(QObject* parent = 0);
|
||||||
~Roster();
|
~Roster();
|
||||||
|
|
||||||
public slots:
|
|
||||||
void addAccount(const QMap<QString, QVariant> &data);
|
void addAccount(const QMap<QString, QVariant> &data);
|
||||||
void updateAccount(const QString& account, const QString& field, const QVariant& value);
|
void updateAccount(const QString& account, const QString& field, const QVariant& value);
|
||||||
void removeAccount(const QString& account);
|
void removeAccount(const QString& account);
|
||||||
@ -66,12 +65,7 @@ public slots:
|
|||||||
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
||||||
|
QString getContactName(const QString& account, const QString& jid);
|
||||||
public:
|
|
||||||
QString getContactName(const QString& account, const QString& jid) const;
|
|
||||||
Item::Type getContactType(const Models::Roster::ElId& id) const;
|
|
||||||
const Element* getElementConst(const ElId& id) const;
|
|
||||||
Element* getElement(const ElId& id);
|
|
||||||
|
|
||||||
QVariant data ( const QModelIndex& index, int role ) const override;
|
QVariant data ( const QModelIndex& index, int role ) const override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
@ -83,13 +77,10 @@ public:
|
|||||||
|
|
||||||
std::deque<QString> groupList(const QString& account) const;
|
std::deque<QString> groupList(const QString& account) const;
|
||||||
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
||||||
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource) const;
|
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource);
|
||||||
Account* getAccount(const QString& name);
|
Account* getAccount(const QString& name);
|
||||||
const Account* getAccountConst(const QString& name) const;
|
|
||||||
QModelIndex getAccountIndex(const QString& name);
|
QModelIndex getAccountIndex(const QString& name);
|
||||||
QModelIndex getGroupIndex(const QString& account, const QString& name);
|
QModelIndex getGroupIndex(const QString& account, const QString& name);
|
||||||
QModelIndex getContactIndex(const QString& account, const QString& jid, const QString& resource = "");
|
|
||||||
bool markMessageAsRead(const ElId& elementId, const QString& messageId);
|
|
||||||
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
void fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
@ -101,13 +92,14 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
void unreadMessagesCountChanged(int count);
|
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element* getElement(const ElId& id);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
|
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
|
||||||
void onAccountReconnected();
|
|
||||||
void onChildChanged(Models::Item* item, int row, int col);
|
void onChildChanged(Models::Item* item, int row, int col);
|
||||||
void onChildIsAboutToBeInserted(Item* parent, int first, int last);
|
void onChildIsAboutToBeInserted(Item* parent, int first, int last);
|
||||||
void onChildInserted();
|
void onChildInserted();
|
||||||
@ -116,8 +108,7 @@ private slots:
|
|||||||
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
|
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
|
||||||
void onChildMoved();
|
void onChildMoved();
|
||||||
void onElementRequestArchive(const QString& before);
|
void onElementRequestArchive(const QString& before);
|
||||||
void recalculateUnreadMessages();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Item* root;
|
Item* root;
|
||||||
std::map<QString, Account*> accounts;
|
std::map<QString, Account*> accounts;
|
||||||
|
695
ui/squawk.cpp
695
ui/squawk.cpp
@ -21,27 +21,25 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) :
|
Squawk::Squawk(QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
m_ui(new Ui::Squawk),
|
m_ui(new Ui::Squawk),
|
||||||
accounts(nullptr),
|
accounts(0),
|
||||||
preferences(nullptr),
|
rosterModel(),
|
||||||
about(nullptr),
|
conversations(),
|
||||||
rosterModel(p_rosterModel),
|
|
||||||
contextMenu(new QMenu()),
|
contextMenu(new QMenu()),
|
||||||
|
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
|
||||||
vCards(),
|
vCards(),
|
||||||
currentConversation(nullptr),
|
requestedAccountsForPasswords(),
|
||||||
|
prompt(0),
|
||||||
|
currentConversation(0),
|
||||||
restoreSelection(),
|
restoreSelection(),
|
||||||
needToRestore(false)
|
needToRestore(false)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
m_ui->roster->setModel(&rosterModel);
|
m_ui->roster->setModel(&rosterModel);
|
||||||
m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu);
|
m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1) {
|
m_ui->roster->setColumnWidth(1, 30);
|
||||||
m_ui->roster->setColumnWidth(1, 52);
|
|
||||||
} else {
|
|
||||||
m_ui->roster->setColumnWidth(1, 26);
|
|
||||||
}
|
|
||||||
m_ui->roster->setIconSize(QSize(20, 20));
|
m_ui->roster->setIconSize(QSize(20, 20));
|
||||||
m_ui->roster->header()->setStretchLastSection(false);
|
m_ui->roster->header()->setStretchLastSection(false);
|
||||||
m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
@ -53,7 +51,6 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) :
|
|||||||
m_ui->comboBox->setCurrentIndex(static_cast<int>(Shared::Availability::offline));
|
m_ui->comboBox->setCurrentIndex(static_cast<int>(Shared::Availability::offline));
|
||||||
|
|
||||||
connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts);
|
connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts);
|
||||||
connect(m_ui->actionPreferences, &QAction::triggered, this, &Squawk::onPreferences);
|
|
||||||
connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact);
|
connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact);
|
||||||
connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference);
|
connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference);
|
||||||
connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::close);
|
connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::close);
|
||||||
@ -62,10 +59,13 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) :
|
|||||||
connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu);
|
connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu);
|
||||||
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
|
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
|
||||||
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
|
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
|
||||||
|
connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage);
|
||||||
|
|
||||||
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
|
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
|
||||||
|
connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive);
|
||||||
|
connect(&rosterModel, &Models::Roster::fileDownloadRequest, this, &Squawk::fileDownloadRequest);
|
||||||
|
connect(&rosterModel, &Models::Roster::localPathInvalid, this, &Squawk::localPathInvalid);
|
||||||
connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide);
|
connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide);
|
||||||
connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled);
|
|
||||||
//m_ui->mainToolBar->addWidget(m_ui->comboBox);
|
//m_ui->mainToolBar->addWidget(m_ui->comboBox);
|
||||||
|
|
||||||
if (testAttribute(Qt::WA_TranslucentBackground)) {
|
if (testAttribute(Qt::WA_TranslucentBackground)) {
|
||||||
@ -95,8 +95,8 @@ Squawk::~Squawk() {
|
|||||||
|
|
||||||
void Squawk::onAccounts()
|
void Squawk::onAccounts()
|
||||||
{
|
{
|
||||||
if (accounts == nullptr) {
|
if (accounts == 0) {
|
||||||
accounts = new Accounts(rosterModel.accountsModel, this);
|
accounts = new Accounts(rosterModel.accountsModel);
|
||||||
accounts->setAttribute(Qt::WA_DeleteOnClose);
|
accounts->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
connect(accounts, &Accounts::destroyed, this, &Squawk::onAccountsClosed);
|
connect(accounts, &Accounts::destroyed, this, &Squawk::onAccountsClosed);
|
||||||
connect(accounts, &Accounts::newAccount, this, &Squawk::newAccountRequest);
|
connect(accounts, &Accounts::newAccount, this, &Squawk::newAccountRequest);
|
||||||
@ -104,26 +104,15 @@ void Squawk::onAccounts()
|
|||||||
connect(accounts, &Accounts::connectAccount, this, &Squawk::connectAccount);
|
connect(accounts, &Accounts::connectAccount, this, &Squawk::connectAccount);
|
||||||
connect(accounts, &Accounts::disconnectAccount, this, &Squawk::disconnectAccount);
|
connect(accounts, &Accounts::disconnectAccount, this, &Squawk::disconnectAccount);
|
||||||
connect(accounts, &Accounts::removeAccount, this, &Squawk::removeAccountRequest);
|
connect(accounts, &Accounts::removeAccount, this, &Squawk::removeAccountRequest);
|
||||||
|
|
||||||
|
accounts->show();
|
||||||
|
} else {
|
||||||
|
accounts->show();
|
||||||
|
accounts->raise();
|
||||||
|
accounts->activateWindow();
|
||||||
}
|
}
|
||||||
accounts->show();
|
|
||||||
accounts->raise();
|
|
||||||
accounts->activateWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onPreferences()
|
|
||||||
{
|
|
||||||
if (preferences == nullptr) {
|
|
||||||
preferences = new Settings(this);
|
|
||||||
preferences->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
connect(preferences, &Settings::destroyed, this, &Squawk::onPreferencesClosed);
|
|
||||||
connect(preferences, &Settings::changeDownloadsPath, this, &Squawk::changeDownloadsPath);
|
|
||||||
}
|
|
||||||
preferences->show();
|
|
||||||
preferences->raise();
|
|
||||||
preferences->activateWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Squawk::onAccountsSizeChanged(unsigned int size)
|
void Squawk::onAccountsSizeChanged(unsigned int size)
|
||||||
{
|
{
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
@ -177,47 +166,143 @@ void Squawk::onJoinConferenceAccepted()
|
|||||||
|
|
||||||
void Squawk::closeEvent(QCloseEvent* event)
|
void Squawk::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
if (accounts != nullptr) {
|
if (accounts != 0) {
|
||||||
accounts->close();
|
accounts->close();
|
||||||
}
|
}
|
||||||
if (preferences != nullptr) {
|
|
||||||
preferences->close();
|
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
|
||||||
}
|
disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||||
if (about != nullptr) {
|
itr->second->close();
|
||||||
about->close();
|
|
||||||
}
|
}
|
||||||
|
conversations.clear();
|
||||||
|
|
||||||
for (std::map<QString, VCard*>::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) {
|
for (std::map<QString, VCard*>::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) {
|
||||||
disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed);
|
disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed);
|
||||||
itr->second->close();
|
itr->second->close();
|
||||||
}
|
}
|
||||||
vCards.clear();
|
vCards.clear();
|
||||||
writeSettings();
|
|
||||||
emit closing();;
|
|
||||||
|
|
||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onAccountsClosed() {
|
|
||||||
accounts = nullptr;}
|
|
||||||
|
|
||||||
void Squawk::onPreferencesClosed() {
|
void Squawk::onAccountsClosed(QObject* parent)
|
||||||
preferences = nullptr;}
|
{
|
||||||
|
accounts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::onAboutSquawkClosed() {
|
void Squawk::newAccount(const QMap<QString, QVariant>& account)
|
||||||
about = nullptr;}
|
{
|
||||||
|
rosterModel.addAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::onComboboxActivated(int index)
|
void Squawk::onComboboxActivated(int index)
|
||||||
{
|
{
|
||||||
Shared::Availability av = Shared::Global::fromInt<Shared::Availability>(index);
|
Shared::Availability av = Shared::Global::fromInt<Shared::Availability>(index);
|
||||||
emit changeState(av);
|
if (av != Shared::Availability::offline) {
|
||||||
|
int size = rosterModel.accountsModel->rowCount(QModelIndex());
|
||||||
|
if (size > 0) {
|
||||||
|
emit changeState(av);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
Models::Account* acc = rosterModel.accountsModel->getAccount(i);
|
||||||
|
if (acc->getState() == Shared::ConnectionState::disconnected) {
|
||||||
|
emit connectAccount(acc->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_ui->comboBox->setCurrentIndex(static_cast<int>(Shared::Availability::offline));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit changeState(av);
|
||||||
|
int size = rosterModel.accountsModel->rowCount(QModelIndex());
|
||||||
|
for (int i = 0; i != size; ++i) {
|
||||||
|
Models::Account* acc = rosterModel.accountsModel->getAccount(i);
|
||||||
|
if (acc->getState() != Shared::ConnectionState::disconnected) {
|
||||||
|
emit disconnectAccount(acc->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::expand(const QModelIndex& index) {
|
void Squawk::changeAccount(const QString& account, const QMap<QString, QVariant>& data)
|
||||||
m_ui->roster->expand(index);}
|
{
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
|
QString attr = itr.key();
|
||||||
|
rosterModel.updateAccount(account, attr, *itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::stateChanged(Shared::Availability state) {
|
void Squawk::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
m_ui->comboBox->setCurrentIndex(static_cast<int>(state));}
|
{
|
||||||
|
rosterModel.addContact(account, jid, group, data);
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = rosterModel.getAccountIndex(account);
|
||||||
|
m_ui->roster->expand(ind);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::addGroup(const QString& account, const QString& name)
|
||||||
|
{
|
||||||
|
rosterModel.addGroup(account, name);
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = rosterModel.getAccountIndex(account);
|
||||||
|
m_ui->roster->expand(ind);
|
||||||
|
if (settings.value(name + "/expanded", false).toBool()) {
|
||||||
|
m_ui->roster->expand(rosterModel.getGroupIndex(account, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removeGroup(const QString& account, const QString& name)
|
||||||
|
{
|
||||||
|
rosterModel.removeGroup(account, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.changeContact(account, jid, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removeContact(const QString& account, const QString& jid)
|
||||||
|
{
|
||||||
|
rosterModel.removeContact(account, jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removeContact(const QString& account, const QString& jid, const QString& group)
|
||||||
|
{
|
||||||
|
rosterModel.removeContact(account, jid, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.addPresence(account, jid, name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removePresence(const QString& account, const QString& jid, const QString& name)
|
||||||
|
{
|
||||||
|
rosterModel.removePresence(account, jid, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::stateChanged(Shared::Availability state)
|
||||||
|
{
|
||||||
|
m_ui->comboBox->setCurrentIndex(static_cast<int>(state));
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
||||||
{
|
{
|
||||||
@ -226,35 +311,194 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
|||||||
if (node->type == Models::Item::reference) {
|
if (node->type == Models::Item::reference) {
|
||||||
node = static_cast<Models::Reference*>(node)->dereference();
|
node = static_cast<Models::Reference*>(node)->dereference();
|
||||||
}
|
}
|
||||||
Models::Contact* contact = nullptr;
|
Models::Contact* contact = 0;
|
||||||
Models::Room* room = nullptr;
|
Models::Room* room = 0;
|
||||||
|
QString res;
|
||||||
|
Models::Roster::ElId* id = 0;
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case Models::Item::contact:
|
case Models::Item::contact:
|
||||||
contact = static_cast<Models::Contact*>(node);
|
contact = static_cast<Models::Contact*>(node);
|
||||||
emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid()));
|
id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid());
|
||||||
break;
|
break;
|
||||||
case Models::Item::presence:
|
case Models::Item::presence:
|
||||||
contact = static_cast<Models::Contact*>(node->parentItem());
|
contact = static_cast<Models::Contact*>(node->parentItem());
|
||||||
emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid()), node->getName());
|
id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid());
|
||||||
|
res = node->getName();
|
||||||
break;
|
break;
|
||||||
case Models::Item::room:
|
case Models::Item::room:
|
||||||
room = static_cast<Models::Room*>(node);
|
room = static_cast<Models::Room*>(node);
|
||||||
emit openConversation(Models::Roster::ElId(room->getAccountName(), room->getJid()));
|
id = new Models::Roster::ElId(room->getAccountName(), room->getJid());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
m_ui->roster->expand(item);
|
m_ui->roster->expand(item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id != 0) {
|
||||||
|
Conversations::const_iterator itr = conversations.find(*id);
|
||||||
|
Models::Account* acc = rosterModel.getAccount(id->account);
|
||||||
|
Conversation* conv = 0;
|
||||||
|
bool created = false;
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conv = itr->second;
|
||||||
|
} else if (contact != 0) {
|
||||||
|
created = true;
|
||||||
|
conv = new Chat(acc, contact);
|
||||||
|
} else if (room != 0) {
|
||||||
|
created = true;
|
||||||
|
conv = new Room(acc, room);
|
||||||
|
|
||||||
|
if (!room->getJoined()) {
|
||||||
|
emit setRoomJoined(id->account, id->name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conv != 0) {
|
||||||
|
if (created) {
|
||||||
|
conv->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
subscribeConversation(conv);
|
||||||
|
conversations.insert(std::make_pair(*id, conv));
|
||||||
|
}
|
||||||
|
|
||||||
|
conv->show();
|
||||||
|
conv->raise();
|
||||||
|
conv->activateWindow();
|
||||||
|
|
||||||
|
if (res.size() > 0) {
|
||||||
|
conv->setPalResource(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::closeCurrentConversation()
|
void Squawk::onConversationClosed(QObject* parent)
|
||||||
{
|
{
|
||||||
if (currentConversation != nullptr) {
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
Models::Roster::ElId id(conv->getAccount(), conv->getJid());
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conversations.erase(itr);
|
||||||
|
}
|
||||||
|
if (conv->isMuc) {
|
||||||
|
Room* room = static_cast<Room*>(conv);
|
||||||
|
if (!room->autoJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up)
|
||||||
|
{
|
||||||
|
rosterModel.fileProgress(msgs, value, up);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path)
|
||||||
|
{
|
||||||
|
rosterModel.fileComplete(msgs, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up)
|
||||||
|
{
|
||||||
|
rosterModel.fileError(msgs, error, up);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path)
|
||||||
|
{
|
||||||
|
rosterModel.fileComplete(msgs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::accountMessage(const QString& account, const Shared::Message& data)
|
||||||
|
{
|
||||||
|
rosterModel.addMessage(account, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onUnnoticedMessage(const QString& account, const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
notify(account, msg); //Telegram does this way - notifies even if the app is visible
|
||||||
|
QApplication::alert(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.changeMessage(account, jid, id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::notify(const QString& account, const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid()));
|
||||||
|
QString path = QString(rosterModel.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource()));
|
||||||
|
QVariantList args;
|
||||||
|
args << QString(QCoreApplication::applicationName());
|
||||||
|
args << QVariant(QVariant::UInt); //TODO some normal id
|
||||||
|
if (path.size() > 0) {
|
||||||
|
args << path;
|
||||||
|
} else {
|
||||||
|
args << QString("mail-message"); //TODO should here better be unknown user icon?
|
||||||
|
}
|
||||||
|
if (msg.getType() == Shared::Message::groupChat) {
|
||||||
|
args << msg.getFromResource() + " from " + name;
|
||||||
|
} else {
|
||||||
|
args << name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString body(msg.getBody());
|
||||||
|
QString oob(msg.getOutOfBandUrl());
|
||||||
|
if (body == oob) {
|
||||||
|
body = tr("Attached file");
|
||||||
|
}
|
||||||
|
|
||||||
|
args << body;
|
||||||
|
args << QStringList();
|
||||||
|
args << QVariantMap();
|
||||||
|
args << 3000;
|
||||||
|
dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onConversationMessage(const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
rosterModel.addMessage(acc, msg);
|
||||||
|
emit sendMessage(acc, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onRequestArchive(const QString& account, const QString& jid, const QString& before)
|
||||||
|
{
|
||||||
|
emit requestArchive(account, jid, 20, before); //TODO amount as a settings value
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last)
|
||||||
|
{
|
||||||
|
rosterModel.responseArchive(account, jid, list, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removeAccount(const QString& account)
|
||||||
|
{
|
||||||
|
Conversations::const_iterator itr = conversations.begin();
|
||||||
|
while (itr != conversations.end()) {
|
||||||
|
if (itr->first.account == account) {
|
||||||
|
Conversations::const_iterator lItr = itr;
|
||||||
|
++itr;
|
||||||
|
Conversation* conv = lItr->second;
|
||||||
|
disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||||
|
conv->close();
|
||||||
|
conversations.erase(lItr);
|
||||||
|
} else {
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentConversation != 0 && currentConversation->getAccount() == account) {
|
||||||
currentConversation->deleteLater();
|
currentConversation->deleteLater();
|
||||||
currentConversation = nullptr;
|
currentConversation = 0;
|
||||||
m_ui->filler->show();
|
m_ui->filler->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rosterModel.removeAccount(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onRosterContextMenu(const QPoint& point)
|
void Squawk::onRosterContextMenu(const QPoint& point)
|
||||||
@ -274,12 +518,17 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
hasMenu = true;
|
hasMenu = true;
|
||||||
QString name = acc->getName();
|
QString name = acc->getName();
|
||||||
|
|
||||||
if (acc->getActive()) {
|
if (acc->getState() != Shared::ConnectionState::disconnected) {
|
||||||
QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Deactivate"));
|
QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Disconnect"));
|
||||||
connect(con, &QAction::triggered, std::bind(&Squawk::disconnectAccount, this, name));
|
con->setEnabled(active);
|
||||||
|
connect(con, &QAction::triggered, [this, name]() {
|
||||||
|
emit disconnectAccount(name);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Activate"));
|
QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Connect"));
|
||||||
connect(con, &QAction::triggered, std::bind(&Squawk::connectAccount, this, name));
|
connect(con, &QAction::triggered, [this, name]() {
|
||||||
|
emit connectAccount(name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
||||||
@ -287,18 +536,22 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true));
|
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true));
|
||||||
|
|
||||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
||||||
connect(remove, &QAction::triggered, std::bind(&Squawk::removeAccountRequest, this, name));
|
remove->setEnabled(active);
|
||||||
|
connect(remove, &QAction::triggered, [this, name]() {
|
||||||
|
emit removeAccount(name);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Models::Item::contact: {
|
case Models::Item::contact: {
|
||||||
Models::Contact* cnt = static_cast<Models::Contact*>(item);
|
Models::Contact* cnt = static_cast<Models::Contact*>(item);
|
||||||
Models::Roster::ElId id(cnt->getAccountName(), cnt->getJid());
|
|
||||||
QString cntName = cnt->getName();
|
|
||||||
hasMenu = true;
|
hasMenu = true;
|
||||||
|
|
||||||
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog"));
|
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog"));
|
||||||
dialog->setEnabled(active);
|
dialog->setEnabled(active);
|
||||||
connect(dialog, &QAction::triggered, std::bind(&Squawk::onRosterItemDoubleClicked, this, index));
|
connect(dialog, &QAction::triggered, [this, index]() {
|
||||||
|
onRosterItemDoubleClicked(index);
|
||||||
|
});
|
||||||
|
|
||||||
Shared::SubscriptionState state = cnt->getState();
|
Shared::SubscriptionState state = cnt->getState();
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@ -306,7 +559,9 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
case Shared::SubscriptionState::to: {
|
case Shared::SubscriptionState::to: {
|
||||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
|
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
|
||||||
unsub->setEnabled(active);
|
unsub->setEnabled(active);
|
||||||
connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false));
|
connect(unsub, &QAction::triggered, [this, cnt]() {
|
||||||
|
emit unsubscribeContact(cnt->getAccountName(), cnt->getJid(), "");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Shared::SubscriptionState::from:
|
case Shared::SubscriptionState::from:
|
||||||
@ -314,68 +569,75 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
case Shared::SubscriptionState::none: {
|
case Shared::SubscriptionState::none: {
|
||||||
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
|
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
|
||||||
sub->setEnabled(active);
|
sub->setEnabled(active);
|
||||||
connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true));
|
connect(sub, &QAction::triggered, [this, cnt]() {
|
||||||
|
emit subscribeContact(cnt->getAccountName(), cnt->getJid(), "");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
QString accName = cnt->getAccountName();
|
||||||
|
QString cntJID = cnt->getJid();
|
||||||
|
QString cntName = cnt->getName();
|
||||||
|
|
||||||
QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename"));
|
QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename"));
|
||||||
rename->setEnabled(active);
|
rename->setEnabled(active);
|
||||||
connect(rename, &QAction::triggered, [this, cntName, id]() {
|
connect(rename, &QAction::triggered, [this, cntName, accName, cntJID]() {
|
||||||
QInputDialog* dialog = new QInputDialog(this);
|
QInputDialog* dialog = new QInputDialog(this);
|
||||||
connect(dialog, &QDialog::accepted, [this, dialog, cntName, id]() {
|
connect(dialog, &QDialog::accepted, [this, dialog, cntName, accName, cntJID]() {
|
||||||
QString newName = dialog->textValue();
|
QString newName = dialog->textValue();
|
||||||
if (newName != cntName) {
|
if (newName != cntName) {
|
||||||
emit renameContactRequest(id.account, id.name, newName);
|
emit renameContactRequest(accName, cntJID, newName);
|
||||||
}
|
}
|
||||||
dialog->deleteLater();
|
dialog->deleteLater();
|
||||||
});
|
});
|
||||||
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
|
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
|
||||||
dialog->setInputMode(QInputDialog::TextInput);
|
dialog->setInputMode(QInputDialog::TextInput);
|
||||||
dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(id.name));
|
dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(cntJID));
|
||||||
dialog->setWindowTitle(tr("Renaming %1").arg(id.name));
|
dialog->setWindowTitle(tr("Renaming %1").arg(cntJID));
|
||||||
dialog->setTextValue(cntName);
|
dialog->setTextValue(cntName);
|
||||||
dialog->exec();
|
dialog->exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups"));
|
QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups"));
|
||||||
std::deque<QString> groupList = rosterModel.groupList(id.account);
|
std::deque<QString> groupList = rosterModel.groupList(accName);
|
||||||
for (QString groupName : groupList) {
|
for (QString groupName : groupList) {
|
||||||
QAction* gr = groupsMenu->addAction(groupName);
|
QAction* gr = groupsMenu->addAction(groupName);
|
||||||
gr->setCheckable(true);
|
gr->setCheckable(true);
|
||||||
gr->setChecked(rosterModel.groupHasContact(id.account, groupName, id.name));
|
gr->setChecked(rosterModel.groupHasContact(accName, groupName, cntJID));
|
||||||
gr->setEnabled(active);
|
gr->setEnabled(active);
|
||||||
connect(gr, &QAction::toggled, [this, groupName, id](bool checked) {
|
connect(gr, &QAction::toggled, [this, accName, groupName, cntJID](bool checked) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
emit addContactToGroupRequest(id.account, id.name, groupName);
|
emit addContactToGroupRequest(accName, cntJID, groupName);
|
||||||
} else {
|
} else {
|
||||||
emit removeContactFromGroupRequest(id.account, id.name, groupName);
|
emit removeContactFromGroupRequest(accName, cntJID, groupName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group"));
|
QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group"));
|
||||||
newGroup->setEnabled(active);
|
newGroup->setEnabled(active);
|
||||||
connect(newGroup, &QAction::triggered, [this, id]() {
|
connect(newGroup, &QAction::triggered, [this, accName, cntJID]() {
|
||||||
QInputDialog* dialog = new QInputDialog(this);
|
QInputDialog* dialog = new QInputDialog(this);
|
||||||
connect(dialog, &QDialog::accepted, [this, dialog, id]() {
|
connect(dialog, &QDialog::accepted, [this, dialog, accName, cntJID]() {
|
||||||
emit addContactToGroupRequest(id.account, id.name, dialog->textValue());
|
emit addContactToGroupRequest(accName, cntJID, dialog->textValue());
|
||||||
dialog->deleteLater();
|
dialog->deleteLater();
|
||||||
});
|
});
|
||||||
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
|
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
|
||||||
dialog->setInputMode(QInputDialog::TextInput);
|
dialog->setInputMode(QInputDialog::TextInput);
|
||||||
dialog->setLabelText(tr("New group name"));
|
dialog->setLabelText(tr("New group name"));
|
||||||
dialog->setWindowTitle(tr("Add %1 to a new group").arg(id.name));
|
dialog->setWindowTitle(tr("Add %1 to a new group").arg(cntJID));
|
||||||
dialog->exec();
|
dialog->exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
||||||
card->setEnabled(active);
|
card->setEnabled(active);
|
||||||
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, id.account, id.name, false));
|
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, accName, cnt->getJid(), false));
|
||||||
|
|
||||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
||||||
remove->setEnabled(active);
|
remove->setEnabled(active);
|
||||||
connect(remove, &QAction::triggered, std::bind(&Squawk::removeContactRequest, this, id.account, id.name));
|
connect(remove, &QAction::triggered, [this, cnt]() {
|
||||||
|
emit removeContactRequest(cnt->getAccountName(), cnt->getJid());
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -394,16 +656,32 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
if (room->getAutoJoin()) {
|
if (room->getAutoJoin()) {
|
||||||
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
|
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
|
||||||
unsub->setEnabled(active);
|
unsub->setEnabled(active);
|
||||||
connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false));
|
connect(unsub, &QAction::triggered, [this, id]() {
|
||||||
|
emit setRoomAutoJoin(id.account, id.name, false);
|
||||||
|
if (conversations.find(id) == conversations.end()
|
||||||
|
&& (currentConversation == 0 || currentConversation->getId() != id)
|
||||||
|
) { //to leave the room if it's not opened in a conversation window
|
||||||
|
emit setRoomJoined(id.account, id.name, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
|
QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
|
||||||
sub->setEnabled(active);
|
unsub->setEnabled(active);
|
||||||
connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true));
|
connect(unsub, &QAction::triggered, [this, id]() {
|
||||||
|
emit setRoomAutoJoin(id.account, id.name, true);
|
||||||
|
if (conversations.find(id) == conversations.end()
|
||||||
|
&& (currentConversation == 0 || currentConversation->getId() != id)
|
||||||
|
) { //to join the room if it's not already joined
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
|
||||||
remove->setEnabled(active);
|
remove->setEnabled(active);
|
||||||
connect(remove, &QAction::triggered, std::bind(&Squawk::removeRoomRequest, this, id.account, id.name));
|
connect(remove, &QAction::triggered, [this, id]() {
|
||||||
|
emit removeRoomRequest(id.account, id.name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -415,6 +693,36 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Squawk::addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.addRoom(account, jid, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.changeRoom(account, jid, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removeRoom(const QString& account, const QString jid)
|
||||||
|
{
|
||||||
|
rosterModel.removeRoom(account, jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.addRoomParticipant(account, jid, name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
rosterModel.changeRoomParticipant(account, jid, name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::removeRoomParticipant(const QString& account, const QString& jid, const QString& name)
|
||||||
|
{
|
||||||
|
rosterModel.removeRoomParticipant(account, jid, name);
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::responseVCard(const QString& jid, const Shared::VCard& card)
|
void Squawk::responseVCard(const QString& jid, const Shared::VCard& card)
|
||||||
{
|
{
|
||||||
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
|
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
|
||||||
@ -472,40 +780,70 @@ void Squawk::onVCardSave(const Shared::VCard& card, const QString& account)
|
|||||||
widget->deleteLater();
|
widget->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Squawk::readSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
|
||||||
|
if (settings.contains("availability")) {
|
||||||
|
int avail = settings.value("availability").toInt();
|
||||||
|
m_ui->comboBox->setCurrentIndex(avail);
|
||||||
|
emit stateChanged(Shared::Global::fromInt<Shared::Availability>(avail));
|
||||||
|
|
||||||
|
int size = settings.beginReadArray("connectedAccounts");
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
emit connectAccount(settings.value("name").toString()); //TODO this is actually not needed, stateChanged event already connects everything you have
|
||||||
|
} // need to fix that
|
||||||
|
settings.endArray();
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::writeSettings()
|
void Squawk::writeSettings()
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.beginGroup("ui");
|
settings.beginGroup("ui");
|
||||||
settings.beginGroup("window");
|
settings.beginGroup("window");
|
||||||
settings.setValue("geometry", saveGeometry());
|
settings.setValue("geometry", saveGeometry());
|
||||||
settings.setValue("state", saveState());
|
settings.setValue("state", saveState());
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.setValue("splitter", m_ui->splitter->saveState());
|
settings.setValue("splitter", m_ui->splitter->saveState());
|
||||||
settings.remove("roster");
|
|
||||||
settings.beginGroup("roster");
|
settings.setValue("availability", m_ui->comboBox->currentIndex());
|
||||||
int size = rosterModel.accountsModel->rowCount(QModelIndex());
|
settings.beginWriteArray("connectedAccounts");
|
||||||
for (int i = 0; i < size; ++i) {
|
int size = rosterModel.accountsModel->rowCount(QModelIndex());
|
||||||
QModelIndex acc = rosterModel.index(i, 0, QModelIndex());
|
for (int i = 0; i < size; ++i) {
|
||||||
Models::Account* account = rosterModel.accountsModel->getAccount(i);
|
Models::Account* acc = rosterModel.accountsModel->getAccount(i);
|
||||||
QString accName = account->getName();
|
if (acc->getState() != Shared::ConnectionState::disconnected) {
|
||||||
settings.beginGroup(accName);
|
settings.setArrayIndex(i);
|
||||||
|
settings.setValue("name", acc->getName());
|
||||||
settings.setValue("expanded", m_ui->roster->isExpanded(acc));
|
}
|
||||||
std::deque<QString> groups = rosterModel.groupList(accName);
|
}
|
||||||
for (const QString& groupName : groups) {
|
settings.endArray();
|
||||||
settings.beginGroup(groupName);
|
|
||||||
QModelIndex gIndex = rosterModel.getGroupIndex(accName, groupName);
|
settings.remove("roster");
|
||||||
settings.setValue("expanded", m_ui->roster->isExpanded(gIndex));
|
settings.beginGroup("roster");
|
||||||
settings.endGroup();
|
for (int i = 0; i < size; ++i) {
|
||||||
}
|
QModelIndex acc = rosterModel.index(i, 0, QModelIndex());
|
||||||
|
Models::Account* account = rosterModel.accountsModel->getAccount(i);
|
||||||
settings.endGroup();
|
QString accName = account->getName();
|
||||||
}
|
settings.beginGroup(accName);
|
||||||
settings.endGroup();
|
|
||||||
|
settings.setValue("expanded", m_ui->roster->isExpanded(acc));
|
||||||
|
std::deque<QString> groups = rosterModel.groupList(accName);
|
||||||
|
for (const QString& groupName : groups) {
|
||||||
|
settings.beginGroup(groupName);
|
||||||
|
QModelIndex gIndex = rosterModel.getGroupIndex(accName, groupName);
|
||||||
|
settings.setValue("expanded", m_ui->roster->isExpanded(gIndex));
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onItemCollepsed(const QModelIndex& index)
|
void Squawk::onItemCollepsed(const QModelIndex& index)
|
||||||
@ -527,6 +865,58 @@ void Squawk::onItemCollepsed(const QModelIndex& index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Squawk::requestPassword(const QString& account)
|
||||||
|
{
|
||||||
|
requestedAccountsForPasswords.push_back(account);
|
||||||
|
checkNextAccountForPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::checkNextAccountForPassword()
|
||||||
|
{
|
||||||
|
if (prompt == 0 && requestedAccountsForPasswords.size() > 0) {
|
||||||
|
prompt = new QInputDialog(this);
|
||||||
|
QString accName = requestedAccountsForPasswords.front();
|
||||||
|
connect(prompt, &QDialog::accepted, this, &Squawk::onPasswordPromptAccepted);
|
||||||
|
connect(prompt, &QDialog::rejected, this, &Squawk::onPasswordPromptRejected);
|
||||||
|
prompt->setInputMode(QInputDialog::TextInput);
|
||||||
|
prompt->setTextEchoMode(QLineEdit::Password);
|
||||||
|
prompt->setLabelText(tr("Input the password for account %1").arg(accName));
|
||||||
|
prompt->setWindowTitle(tr("Password for account %1").arg(accName));
|
||||||
|
prompt->setTextValue("");
|
||||||
|
prompt->exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onPasswordPromptAccepted()
|
||||||
|
{
|
||||||
|
emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue());
|
||||||
|
onPasswordPromptDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onPasswordPromptDone()
|
||||||
|
{
|
||||||
|
prompt->deleteLater();
|
||||||
|
prompt = 0;
|
||||||
|
requestedAccountsForPasswords.pop_front();
|
||||||
|
checkNextAccountForPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onPasswordPromptRejected()
|
||||||
|
{
|
||||||
|
//for now it's the same on reject and on accept, but one day I'm gonna make
|
||||||
|
//"Asking for the password again on the authentication failure" feature
|
||||||
|
//and here I'll be able to break the circle of password requests
|
||||||
|
emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue());
|
||||||
|
onPasswordPromptDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::subscribeConversation(Conversation* conv)
|
||||||
|
{
|
||||||
|
connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||||
|
connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage);
|
||||||
|
connect(conv, &Conversation::notifyableMessage, this, &Squawk::notify);
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
|
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
|
||||||
{
|
{
|
||||||
if (restoreSelection.isValid() && restoreSelection == current) {
|
if (restoreSelection.isValid() && restoreSelection == current) {
|
||||||
@ -539,10 +929,10 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
if (node->type == Models::Item::reference) {
|
if (node->type == Models::Item::reference) {
|
||||||
node = static_cast<Models::Reference*>(node)->dereference();
|
node = static_cast<Models::Reference*>(node)->dereference();
|
||||||
}
|
}
|
||||||
Models::Contact* contact = nullptr;
|
Models::Contact* contact = 0;
|
||||||
Models::Room* room = nullptr;
|
Models::Room* room = 0;
|
||||||
QString res;
|
QString res;
|
||||||
Models::Roster::ElId* id = nullptr;
|
Models::Roster::ElId* id = 0;
|
||||||
bool hasContext = true;
|
bool hasContext = true;
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case Models::Item::contact:
|
case Models::Item::contact:
|
||||||
@ -571,7 +961,7 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasContext && QGuiApplication::mouseButtons() & Qt::RightButton) {
|
if (hasContext && QGuiApplication::mouseButtons() & Qt::RightButton) {
|
||||||
if (id != nullptr) {
|
if (id != 0) {
|
||||||
delete id;
|
delete id;
|
||||||
}
|
}
|
||||||
needToRestore = true;
|
needToRestore = true;
|
||||||
@ -579,10 +969,10 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id != nullptr) {
|
if (id != 0) {
|
||||||
if (currentConversation != nullptr) {
|
if (currentConversation != 0) {
|
||||||
if (currentConversation->getId() == *id) {
|
if (currentConversation->getId() == *id) {
|
||||||
if (contact != nullptr) {
|
if (contact != 0) {
|
||||||
currentConversation->setPalResource(res);
|
currentConversation->setPalResource(res);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -594,16 +984,20 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
Models::Account* acc = rosterModel.getAccount(id->account);
|
Models::Account* acc = rosterModel.getAccount(id->account);
|
||||||
if (contact != nullptr) {
|
if (contact != 0) {
|
||||||
currentConversation = new Chat(acc, contact);
|
currentConversation = new Chat(acc, contact);
|
||||||
} else if (room != nullptr) {
|
} else if (room != 0) {
|
||||||
currentConversation = new Room(acc, room);
|
currentConversation = new Room(acc, room);
|
||||||
|
|
||||||
|
if (!room->getJoined()) {
|
||||||
|
emit setRoomJoined(id->account, id->name, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!testAttribute(Qt::WA_TranslucentBackground)) {
|
if (!testAttribute(Qt::WA_TranslucentBackground)) {
|
||||||
currentConversation->setFeedFrames(true, false, true, true);
|
currentConversation->setFeedFrames(true, false, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit openedConversation();
|
subscribeConversation(currentConversation);
|
||||||
|
|
||||||
if (res.size() > 0) {
|
if (res.size() > 0) {
|
||||||
currentConversation->setPalResource(res);
|
currentConversation->setPalResource(res);
|
||||||
@ -613,10 +1007,18 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
|
|
||||||
delete id;
|
delete id;
|
||||||
} else {
|
} else {
|
||||||
closeCurrentConversation();
|
if (currentConversation != 0) {
|
||||||
|
currentConversation->deleteLater();
|
||||||
|
currentConversation = 0;
|
||||||
|
m_ui->filler->show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
closeCurrentConversation();
|
if (currentConversation != 0) {
|
||||||
|
currentConversation->deleteLater();
|
||||||
|
currentConversation = 0;
|
||||||
|
m_ui->filler->show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,30 +1029,3 @@ void Squawk::onContextAboutToHide()
|
|||||||
m_ui->roster->selectionModel()->setCurrentIndex(restoreSelection, QItemSelectionModel::ClearAndSelect);
|
m_ui->roster->selectionModel()->setCurrentIndex(restoreSelection, QItemSelectionModel::ClearAndSelect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onAboutSquawkCalled()
|
|
||||||
{
|
|
||||||
if (about == nullptr) {
|
|
||||||
about = new About(this);
|
|
||||||
about->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
connect(about, &Settings::destroyed, this, &Squawk::onAboutSquawkClosed);
|
|
||||||
}
|
|
||||||
about->show();
|
|
||||||
about->raise();
|
|
||||||
about->activateWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
Models::Roster::ElId Squawk::currentConversationId() const
|
|
||||||
{
|
|
||||||
if (currentConversation == nullptr) {
|
|
||||||
return Models::Roster::ElId();
|
|
||||||
} else {
|
|
||||||
return Models::Roster::ElId(currentConversation->getAccount(), currentConversation->getJid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Squawk::select(QModelIndex index)
|
|
||||||
{
|
|
||||||
m_ui->roster->scrollTo(index, QAbstractItemView::EnsureVisible);
|
|
||||||
m_ui->roster->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
|
|
||||||
}
|
|
||||||
|
98
ui/squawk.h
98
ui/squawk.h
@ -22,6 +22,7 @@
|
|||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
|
#include <QtDBus/QDBusInterface>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
|
||||||
@ -30,106 +31,137 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "widgets/accounts/accounts.h"
|
#include "widgets/accounts.h"
|
||||||
#include "widgets/chat.h"
|
#include "widgets/chat.h"
|
||||||
#include "widgets/room.h"
|
#include "widgets/room.h"
|
||||||
#include "widgets/newcontact.h"
|
#include "widgets/newcontact.h"
|
||||||
#include "widgets/joinconference.h"
|
#include "widgets/joinconference.h"
|
||||||
#include "models/roster.h"
|
#include "models/roster.h"
|
||||||
#include "widgets/vcard/vcard.h"
|
#include "widgets/vcard/vcard.h"
|
||||||
#include "widgets/settings/settings.h"
|
|
||||||
#include "widgets/about.h"
|
|
||||||
|
|
||||||
#include "shared/shared.h"
|
#include "shared.h"
|
||||||
#include "shared/global.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class Squawk;
|
class Squawk;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Application;
|
|
||||||
|
|
||||||
class Squawk : public QMainWindow
|
class Squawk : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
friend class Application;
|
|
||||||
public:
|
public:
|
||||||
explicit Squawk(Models::Roster& rosterModel, QWidget *parent = nullptr);
|
explicit Squawk(QWidget *parent = nullptr);
|
||||||
~Squawk() override;
|
~Squawk() override;
|
||||||
|
|
||||||
|
void writeSettings();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closing();
|
|
||||||
void newAccountRequest(const QMap<QString, QVariant>&);
|
void newAccountRequest(const QMap<QString, QVariant>&);
|
||||||
|
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
||||||
void removeAccountRequest(const QString&);
|
void removeAccountRequest(const QString&);
|
||||||
void connectAccount(const QString&);
|
void connectAccount(const QString&);
|
||||||
void disconnectAccount(const QString&);
|
void disconnectAccount(const QString&);
|
||||||
void changeState(Shared::Availability state);
|
void changeState(Shared::Availability state);
|
||||||
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
|
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
||||||
|
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
void removeContactRequest(const QString& account, const QString& jid);
|
void removeContactRequest(const QString& account, const QString& jid);
|
||||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||||
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
|
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||||
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void removeRoomRequest(const QString& account, const QString& jid);
|
void removeRoomRequest(const QString& account, const QString& jid);
|
||||||
|
void fileDownloadRequest(const QString& url);
|
||||||
void requestVCard(const QString& account, const QString& jid);
|
void requestVCard(const QString& account, const QString& jid);
|
||||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||||
void changeDownloadsPath(const QString& path);
|
void responsePassword(const QString& account, const QString& password);
|
||||||
|
void localPathInvalid(const QString& path);
|
||||||
void notify(const QString& account, const Shared::Message& msg);
|
|
||||||
void changeSubscription(const Models::Roster::ElId& id, bool subscribe);
|
|
||||||
void openedConversation();
|
|
||||||
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
|
|
||||||
|
|
||||||
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
|
||||||
|
|
||||||
public:
|
|
||||||
Models::Roster::ElId currentConversationId() const;
|
|
||||||
void closeCurrentConversation();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void writeSettings();
|
void readSettings();
|
||||||
|
void newAccount(const QMap<QString, QVariant>& account);
|
||||||
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
|
void removeAccount(const QString& account);
|
||||||
|
void addGroup(const QString& account, const QString& name);
|
||||||
|
void removeGroup(const QString& account, const QString& name);
|
||||||
|
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
|
void removeContact(const QString& account, const QString& jid, const QString& group);
|
||||||
|
void removeContact(const QString& account, const QString& jid);
|
||||||
|
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
||||||
|
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
|
void removePresence(const QString& account, const QString& jid, const QString& name);
|
||||||
void stateChanged(Shared::Availability state);
|
void stateChanged(Shared::Availability state);
|
||||||
|
void accountMessage(const QString& account, const Shared::Message& data);
|
||||||
|
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
|
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
|
void changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
|
void removeRoom(const QString& account, const QString jid);
|
||||||
|
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
|
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
|
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
||||||
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void select(QModelIndex index);
|
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
void requestPassword(const QString& account);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
||||||
QScopedPointer<Ui::Squawk> m_ui;
|
QScopedPointer<Ui::Squawk> m_ui;
|
||||||
|
|
||||||
Accounts* accounts;
|
Accounts* accounts;
|
||||||
Settings* preferences;
|
Models::Roster rosterModel;
|
||||||
About* about;
|
Conversations conversations;
|
||||||
Models::Roster& rosterModel;
|
|
||||||
QMenu* contextMenu;
|
QMenu* contextMenu;
|
||||||
|
QDBusInterface dbus;
|
||||||
std::map<QString, VCard*> vCards;
|
std::map<QString, VCard*> vCards;
|
||||||
|
std::deque<QString> requestedAccountsForPasswords;
|
||||||
|
QInputDialog* prompt;
|
||||||
Conversation* currentConversation;
|
Conversation* currentConversation;
|
||||||
QModelIndex restoreSelection;
|
QModelIndex restoreSelection;
|
||||||
bool needToRestore;
|
bool needToRestore;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent * event) override;
|
void closeEvent(QCloseEvent * event) override;
|
||||||
void expand(const QModelIndex& index);
|
|
||||||
|
protected slots:
|
||||||
|
void notify(const QString& account, const Shared::Message& msg);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAccounts();
|
void onAccounts();
|
||||||
void onPreferences();
|
|
||||||
void onNewContact();
|
void onNewContact();
|
||||||
void onNewConference();
|
void onNewConference();
|
||||||
void onNewContactAccepted();
|
void onNewContactAccepted();
|
||||||
void onJoinConferenceAccepted();
|
void onJoinConferenceAccepted();
|
||||||
void onAccountsSizeChanged(unsigned int size);
|
void onAccountsSizeChanged(unsigned int size);
|
||||||
void onAccountsClosed();
|
void onAccountsClosed(QObject* parent = 0);
|
||||||
void onPreferencesClosed();
|
void onConversationClosed(QObject* parent = 0);
|
||||||
void onVCardClosed();
|
void onVCardClosed();
|
||||||
void onVCardSave(const Shared::VCard& card, const QString& account);
|
void onVCardSave(const Shared::VCard& card, const QString& account);
|
||||||
void onActivateVCard(const QString& account, const QString& jid, bool edition = false);
|
void onActivateVCard(const QString& account, const QString& jid, bool edition = false);
|
||||||
void onComboboxActivated(int index);
|
void onComboboxActivated(int index);
|
||||||
void onRosterItemDoubleClicked(const QModelIndex& item);
|
void onRosterItemDoubleClicked(const QModelIndex& item);
|
||||||
|
void onConversationMessage(const Shared::Message& msg);
|
||||||
|
void onRequestArchive(const QString& account, const QString& jid, const QString& before);
|
||||||
void onRosterContextMenu(const QPoint& point);
|
void onRosterContextMenu(const QPoint& point);
|
||||||
void onItemCollepsed(const QModelIndex& index);
|
void onItemCollepsed(const QModelIndex& index);
|
||||||
|
void onPasswordPromptAccepted();
|
||||||
|
void onPasswordPromptRejected();
|
||||||
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||||
void onContextAboutToHide();
|
void onContextAboutToHide();
|
||||||
void onAboutSquawkCalled();
|
|
||||||
void onAboutSquawkClosed();
|
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void checkNextAccountForPassword();
|
||||||
|
void onPasswordPromptDone();
|
||||||
|
void subscribeConversation(Conversation* conv);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SQUAWK_H
|
#endif // SQUAWK_H
|
||||||
|
65
ui/squawk.ui
65
ui/squawk.ui
@ -73,9 +73,6 @@
|
|||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QTreeView" name="roster">
|
<widget class="QTreeView" name="roster">
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
@ -99,31 +96,16 @@
|
|||||||
<property name="allColumnsShowFocus">
|
<property name="allColumnsShowFocus">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="headerHidden">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="expandsOnDoubleClick">
|
<property name="expandsOnDoubleClick">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="headerVisible">
|
<attribute name="headerVisible">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="headerMinimumSectionSize">
|
|
||||||
<number>10</number>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="headerStretchLastSection">
|
|
||||||
<bool>false</bool>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QComboBox" name="comboBox">
|
<widget class="QComboBox" name="comboBox">
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="editable">
|
<property name="editable">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -133,9 +115,6 @@
|
|||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>-1</number>
|
<number>-1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="frame">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -183,7 +162,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>718</width>
|
<width>718</width>
|
||||||
<height>32</height>
|
<height>27</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuSettings">
|
<widget class="QMenu" name="menuSettings">
|
||||||
@ -191,7 +170,6 @@
|
|||||||
<string>Settings</string>
|
<string>Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="actionAccounts"/>
|
<addaction name="actionAccounts"/>
|
||||||
<addaction name="actionPreferences"/>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -201,20 +179,13 @@
|
|||||||
<addaction name="actionAddConference"/>
|
<addaction name="actionAddConference"/>
|
||||||
<addaction name="actionQuit"/>
|
<addaction name="actionQuit"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuHelp">
|
|
||||||
<property name="title">
|
|
||||||
<string>Help</string>
|
|
||||||
</property>
|
|
||||||
<addaction name="actionAboutSquawk"/>
|
|
||||||
</widget>
|
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
<addaction name="menuSettings"/>
|
<addaction name="menuSettings"/>
|
||||||
<addaction name="menuHelp"/>
|
|
||||||
</widget>
|
</widget>
|
||||||
<action name="actionAccounts">
|
<action name="actionAccounts">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="system-users" resource="../resources/resources.qrc">
|
<iconset theme="system-users">
|
||||||
<normaloff>:/images/fallback/dark/big/group.svg</normaloff>:/images/fallback/dark/big/group.svg</iconset>
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Accounts</string>
|
<string>Accounts</string>
|
||||||
@ -222,8 +193,8 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionQuit">
|
<action name="actionQuit">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="application-exit" resource="../resources/resources.qrc">
|
<iconset theme="application-exit">
|
||||||
<normaloff>:/images/fallback/dark/big/edit-none.svg</normaloff>:/images/fallback/dark/big/edit-none.svg</iconset>
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Quit</string>
|
<string>Quit</string>
|
||||||
@ -234,8 +205,8 @@
|
|||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="list-add-user" resource="../resources/resources.qrc">
|
<iconset theme="list-add-user">
|
||||||
<normaloff>:/images/fallback/dark/big/add.svg</normaloff>:/images/fallback/dark/big/add.svg</iconset>
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Add contact</string>
|
<string>Add contact</string>
|
||||||
@ -246,30 +217,14 @@
|
|||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="resource-group-new" resource="../resources/resources.qrc">
|
<iconset theme="resource-group-new">
|
||||||
<normaloff>:/images/fallback/dark/big/group-new.svg</normaloff>:/images/fallback/dark/big/group-new.svg</iconset>
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Add conference</string>
|
<string>Add conference</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionPreferences">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="settings-configure">
|
|
||||||
<normaloff>.</normaloff>.</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Preferences</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionAboutSquawk">
|
|
||||||
<property name="text">
|
|
||||||
<string>About Squawk</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources/>
|
||||||
<include location="../resources/resources.qrc"/>
|
|
||||||
</resources>
|
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
target_sources(squawk PRIVATE
|
cmake_minimum_required(VERSION 3.3)
|
||||||
badge.cpp
|
project(squawkUIUtils)
|
||||||
badge.h
|
|
||||||
comboboxdelegate.cpp
|
# Instruct CMake to run moc automatically when needed.
|
||||||
comboboxdelegate.h
|
set(CMAKE_AUTOMOC ON)
|
||||||
exponentialblur.cpp
|
# Instruct CMake to create code from Qt designer ui files
|
||||||
exponentialblur.h
|
set(CMAKE_AUTOUIC ON)
|
||||||
flowlayout.cpp
|
|
||||||
flowlayout.h
|
# Find the QtWidgets library
|
||||||
image.cpp
|
find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets Core)
|
||||||
image.h
|
|
||||||
progress.cpp
|
set(squawkUIUtils_SRC
|
||||||
progress.h
|
# messageline.cpp
|
||||||
|
# message.cpp
|
||||||
resizer.cpp
|
resizer.cpp
|
||||||
resizer.h
|
# image.cpp
|
||||||
|
flowlayout.cpp
|
||||||
|
badge.cpp
|
||||||
|
progress.cpp
|
||||||
|
comboboxdelegate.cpp
|
||||||
|
feedview.cpp
|
||||||
|
messagedelegate.cpp
|
||||||
|
exponentialblur.cpp
|
||||||
shadowoverlay.cpp
|
shadowoverlay.cpp
|
||||||
shadowoverlay.h
|
)
|
||||||
)
|
|
||||||
|
# Tell CMake to create the helloworld executable
|
||||||
|
add_library(squawkUIUtils STATIC ${squawkUIUtils_SRC})
|
||||||
|
|
||||||
|
# Use the Widgets module from Qt 5.
|
||||||
|
target_link_libraries(squawkUIUtils squawkWidgets)
|
||||||
|
target_link_libraries(squawkUIUtils Qt5::Widgets)
|
||||||
|
@ -26,62 +26,27 @@ Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWid
|
|||||||
closeButton(new QPushButton()),
|
closeButton(new QPushButton()),
|
||||||
layout(new QHBoxLayout(this))
|
layout(new QHBoxLayout(this))
|
||||||
{
|
{
|
||||||
createMandatoryComponents();
|
setBackgroundRole(QPalette::Base);
|
||||||
|
//setAutoFillBackground(true);
|
||||||
|
setFrameStyle(QFrame::StyledPanel);
|
||||||
|
setFrameShadow(QFrame::Raised);
|
||||||
|
|
||||||
image->setPixmap(icon.pixmap(25, 25));
|
image->setPixmap(icon.pixmap(25, 25));
|
||||||
|
closeButton->setIcon(QIcon::fromTheme("tab-close"));
|
||||||
|
closeButton->setMaximumHeight(25);
|
||||||
|
closeButton->setMaximumWidth(25);
|
||||||
|
|
||||||
layout->addWidget(image);
|
layout->addWidget(image);
|
||||||
layout->addWidget(text);
|
layout->addWidget(text);
|
||||||
layout->addWidget(closeButton);
|
layout->addWidget(closeButton);
|
||||||
}
|
|
||||||
|
layout->setContentsMargins(2, 2, 2, 2);
|
||||||
Badge::Badge(QWidget* parent):
|
|
||||||
QFrame(parent),
|
connect(closeButton, &QPushButton::clicked, this, &Badge::close);
|
||||||
id(Shared::generateUUID()),
|
|
||||||
image(nullptr),
|
|
||||||
text(nullptr),
|
|
||||||
closeButton(new QPushButton()),
|
|
||||||
layout(new QHBoxLayout(this))
|
|
||||||
{
|
|
||||||
createMandatoryComponents();
|
|
||||||
|
|
||||||
layout->addWidget(closeButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Badge::setIcon(const QIcon& icon)
|
|
||||||
{
|
|
||||||
if (image == nullptr) {
|
|
||||||
image = new QLabel();
|
|
||||||
image->setPixmap(icon.pixmap(25, 25));
|
|
||||||
layout->insertWidget(0, image);
|
|
||||||
} else {
|
|
||||||
image->setPixmap(icon.pixmap(25, 25));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Badge::setText(const QString& p_text)
|
|
||||||
{
|
|
||||||
if (text == nullptr) {
|
|
||||||
text = new QLabel(p_text);
|
|
||||||
int index = 0;
|
|
||||||
if (image != nullptr) {
|
|
||||||
index = 1;
|
|
||||||
}
|
|
||||||
layout->insertWidget(index, text);
|
|
||||||
} else {
|
|
||||||
text->setText(p_text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Badge::~Badge()
|
Badge::~Badge()
|
||||||
{
|
{
|
||||||
if (image != nullptr) {
|
|
||||||
delete image;
|
|
||||||
}
|
|
||||||
if (text != nullptr) {
|
|
||||||
delete text;
|
|
||||||
}
|
|
||||||
delete closeButton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Badge::Comparator::operator()(const Badge* a, const Badge* b) const
|
bool Badge::Comparator::operator()(const Badge* a, const Badge* b) const
|
||||||
@ -93,22 +58,3 @@ bool Badge::Comparator::operator()(const Badge& a, const Badge& b) const
|
|||||||
{
|
{
|
||||||
return a.id < b.id;
|
return a.id < b.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Badge::createMandatoryComponents()
|
|
||||||
{
|
|
||||||
setBackgroundRole(QPalette::Base);
|
|
||||||
//setAutoFillBackground(true);
|
|
||||||
setFrameStyle(QFrame::StyledPanel);
|
|
||||||
setFrameShadow(QFrame::Raised);
|
|
||||||
|
|
||||||
QIcon tabCloseIcon = QIcon::fromTheme("tab-close");
|
|
||||||
if (tabCloseIcon.isNull()) {
|
|
||||||
tabCloseIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/edit-none.svg"), QSize(), QIcon::Normal, QIcon::Off);
|
|
||||||
}
|
|
||||||
closeButton->setIcon(tabCloseIcon);
|
|
||||||
|
|
||||||
closeButton->setMaximumHeight(25);
|
|
||||||
closeButton->setMaximumWidth(25);
|
|
||||||
layout->setContentsMargins(2, 2, 2, 2);
|
|
||||||
connect(closeButton, &QPushButton::clicked, this, &Badge::close);
|
|
||||||
}
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include "shared/utils.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
*/
|
*/
|
||||||
@ -35,14 +33,9 @@ class Badge : public QFrame
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Badge(const QString& id, const QString& text, const QIcon& icon, QWidget* parent = nullptr);
|
Badge(const QString& id, const QString& text, const QIcon& icon, QWidget* parent = nullptr);
|
||||||
Badge(QWidget* parent = nullptr);
|
|
||||||
~Badge();
|
~Badge();
|
||||||
|
|
||||||
const QString id;
|
const QString id;
|
||||||
|
|
||||||
public:
|
|
||||||
void setText(const QString& text);
|
|
||||||
void setIcon(const QIcon& icon);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void close();
|
void close();
|
||||||
@ -52,9 +45,6 @@ private:
|
|||||||
QLabel* text;
|
QLabel* text;
|
||||||
QPushButton* closeButton;
|
QPushButton* closeButton;
|
||||||
QHBoxLayout* layout;
|
QHBoxLayout* layout;
|
||||||
|
|
||||||
private:
|
|
||||||
void createMandatoryComponents();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Comparator {
|
struct Comparator {
|
||||||
|
@ -21,17 +21,14 @@
|
|||||||
#include <QPaintEvent>
|
#include <QPaintEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QApplication>
|
|
||||||
#include <QClipboard>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "messagedelegate.h"
|
#include "messagedelegate.h"
|
||||||
#include "messagefeed.h"
|
#include "ui/models/messagefeed.h"
|
||||||
|
|
||||||
constexpr int maxMessageHeight = 10000;
|
constexpr int maxMessageHeight = 10000;
|
||||||
constexpr int approximateSingleMessageHeight = 20;
|
constexpr int approximateSingleMessageHeight = 20;
|
||||||
constexpr int progressSize = 70;
|
constexpr int progressSize = 70;
|
||||||
constexpr int dateDeviderMargin = 10;
|
|
||||||
|
|
||||||
const std::set<int> FeedView::geometryChangingRoles = {
|
const std::set<int> FeedView::geometryChangingRoles = {
|
||||||
Models::MessageFeed::Attach,
|
Models::MessageFeed::Attach,
|
||||||
@ -45,20 +42,11 @@ FeedView::FeedView(QWidget* parent):
|
|||||||
QAbstractItemView(parent),
|
QAbstractItemView(parent),
|
||||||
hints(),
|
hints(),
|
||||||
vo(0),
|
vo(0),
|
||||||
elementMargin(0),
|
|
||||||
specialDelegate(false),
|
specialDelegate(false),
|
||||||
specialModel(false),
|
specialModel(false),
|
||||||
clearWidgetsMode(false),
|
clearWidgetsMode(false),
|
||||||
modelState(Models::MessageFeed::complete),
|
modelState(Models::MessageFeed::complete),
|
||||||
progress(),
|
progress()
|
||||||
dividerFont(),
|
|
||||||
dividerMetrics(dividerFont),
|
|
||||||
mousePressed(false),
|
|
||||||
dragging(false),
|
|
||||||
hovered(Shared::Hover::nothing),
|
|
||||||
dragStartPoint(),
|
|
||||||
dragEndPoint(),
|
|
||||||
selectedText()
|
|
||||||
{
|
{
|
||||||
horizontalScrollBar()->setRange(0, 0);
|
horizontalScrollBar()->setRange(0, 0);
|
||||||
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
||||||
@ -68,15 +56,6 @@ FeedView::FeedView(QWidget* parent):
|
|||||||
|
|
||||||
progress.setParent(viewport());
|
progress.setParent(viewport());
|
||||||
progress.resize(progressSize, progressSize);
|
progress.resize(progressSize, progressSize);
|
||||||
|
|
||||||
dividerFont = getFont();
|
|
||||||
dividerFont.setBold(true);
|
|
||||||
float ndps = dividerFont.pointSizeF();
|
|
||||||
if (ndps != -1) {
|
|
||||||
dividerFont.setPointSizeF(ndps * 1.2);
|
|
||||||
} else {
|
|
||||||
dividerFont.setPointSize(dividerFont.pointSize() + 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedView::~FeedView()
|
FeedView::~FeedView()
|
||||||
@ -115,7 +94,7 @@ QRect FeedView::visualRect(const QModelIndex& index) const
|
|||||||
} else {
|
} else {
|
||||||
const Hint& hint = hints.at(row);
|
const Hint& hint = hints.at(row);
|
||||||
const QWidget* vp = viewport();
|
const QWidget* vp = viewport();
|
||||||
return QRect(hint.x, vp->height() - hint.height - hint.offset + vo, hint.width, hint.height);
|
return QRect(0, vp->height() - hint.height - hint.offset + vo, vp->width(), hint.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +149,7 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom
|
|||||||
|
|
||||||
void FeedView::updateGeometries()
|
void FeedView::updateGeometries()
|
||||||
{
|
{
|
||||||
//qDebug() << "updateGeometries";
|
qDebug() << "updateGeometries";
|
||||||
QScrollBar* bar = verticalScrollBar();
|
QScrollBar* bar = verticalScrollBar();
|
||||||
|
|
||||||
const QStyle* st = style();
|
const QStyle* st = style();
|
||||||
@ -207,40 +186,19 @@ void FeedView::updateGeometries()
|
|||||||
option.rect.setWidth(layoutBounds.width());
|
option.rect.setWidth(layoutBounds.width());
|
||||||
|
|
||||||
hints.clear();
|
hints.clear();
|
||||||
uint32_t previousOffset = elementMargin;
|
uint32_t previousOffset = 0;
|
||||||
QDateTime lastDate;
|
|
||||||
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
||||||
QModelIndex index = m->index(i, 0, rootIndex());
|
QModelIndex index = m->index(i, 0, rootIndex());
|
||||||
QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime();
|
int height = itemDelegate(index)->sizeHint(option, index).height();
|
||||||
if (i > 0) {
|
|
||||||
if (currentDate.daysTo(lastDate) > 0) {
|
|
||||||
previousOffset += dividerMetrics.height() + dateDeviderMargin * 2;
|
|
||||||
} else {
|
|
||||||
previousOffset += elementMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastDate = currentDate;
|
|
||||||
QSize messageSize = itemDelegate(index)->sizeHint(option, index);
|
|
||||||
uint32_t offsetX(0);
|
|
||||||
if (specialDelegate) {
|
|
||||||
if (index.data(Models::MessageFeed::SentByMe).toBool()) {
|
|
||||||
offsetX = layoutBounds.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2;
|
|
||||||
} else {
|
|
||||||
offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hints.emplace_back(Hint({
|
hints.emplace_back(Hint({
|
||||||
false,
|
false,
|
||||||
previousOffset,
|
previousOffset,
|
||||||
static_cast<uint32_t>(messageSize.height()),
|
static_cast<uint32_t>(height)
|
||||||
static_cast<uint32_t>(messageSize.width()),
|
|
||||||
offsetX
|
|
||||||
}));
|
}));
|
||||||
previousOffset += messageSize.height();
|
previousOffset += height;
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalHeight = previousOffset - layoutBounds.height() + dividerMetrics.height() + dateDeviderMargin * 2;
|
int totalHeight = previousOffset - layoutBounds.height();
|
||||||
if (modelState != Models::MessageFeed::complete) {
|
if (modelState != Models::MessageFeed::complete) {
|
||||||
totalHeight += progressSize;
|
totalHeight += progressSize;
|
||||||
}
|
}
|
||||||
@ -262,49 +220,25 @@ void FeedView::updateGeometries()
|
|||||||
|
|
||||||
bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight)
|
bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight)
|
||||||
{
|
{
|
||||||
uint32_t previousOffset = elementMargin;
|
uint32_t previousOffset = 0;
|
||||||
QDateTime lastDate;
|
bool success = true;
|
||||||
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
||||||
QModelIndex index = m->index(i, 0, rootIndex());
|
QModelIndex index = m->index(i, 0, rootIndex());
|
||||||
QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime();
|
int height = itemDelegate(index)->sizeHint(option, index).height();
|
||||||
if (i > 0) {
|
|
||||||
if (currentDate.daysTo(lastDate) > 0) {
|
|
||||||
previousOffset += dateDeviderMargin * 2 + dividerMetrics.height();
|
|
||||||
} else {
|
|
||||||
previousOffset += elementMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastDate = currentDate;
|
|
||||||
QSize messageSize = itemDelegate(index)->sizeHint(option, index);
|
|
||||||
|
|
||||||
if (previousOffset + messageSize.height() + elementMargin > totalHeight) {
|
if (previousOffset + height > totalHeight) {
|
||||||
return false;
|
success = false;
|
||||||
}
|
break;
|
||||||
|
|
||||||
uint32_t offsetX(0);
|
|
||||||
if (specialDelegate) {
|
|
||||||
if (index.data(Models::MessageFeed::SentByMe).toBool()) {
|
|
||||||
offsetX = option.rect.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2;
|
|
||||||
} else {
|
|
||||||
offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hints.emplace_back(Hint({
|
hints.emplace_back(Hint({
|
||||||
false,
|
false,
|
||||||
previousOffset,
|
previousOffset,
|
||||||
static_cast<uint32_t>(messageSize.height()),
|
static_cast<uint32_t>(height)
|
||||||
static_cast<uint32_t>(messageSize.width()),
|
|
||||||
offsetX
|
|
||||||
}));
|
}));
|
||||||
previousOffset += messageSize.height();
|
previousOffset += height;
|
||||||
}
|
|
||||||
|
|
||||||
previousOffset += dateDeviderMargin * 2 + dividerMetrics.height();
|
|
||||||
if (previousOffset > totalHeight) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -332,7 +266,6 @@ void FeedView::paintEvent(QPaintEvent* event)
|
|||||||
toRener.emplace_back(m->index(i, 0, rootIndex()));
|
toRener.emplace_back(m->index(i, 0, rootIndex()));
|
||||||
}
|
}
|
||||||
if (y1 > relativeY1) {
|
if (y1 > relativeY1) {
|
||||||
inZone = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,36 +282,11 @@ void FeedView::paintEvent(QPaintEvent* event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime lastDate;
|
|
||||||
bool first = true;
|
|
||||||
QRect viewportRect = vp->rect();
|
|
||||||
for (const QModelIndex& index : toRener) {
|
for (const QModelIndex& index : toRener) {
|
||||||
QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime();
|
|
||||||
option.rect = visualRect(index);
|
option.rect = visualRect(index);
|
||||||
if (first) {
|
bool mouseOver = option.rect.contains(cursor) && vp->rect().contains(cursor);
|
||||||
int ind = index.row() - 1;
|
|
||||||
if (ind > 0) {
|
|
||||||
QDateTime underDate = m->index(ind, 0, rootIndex()).data(Models::MessageFeed::Date).toDateTime();
|
|
||||||
if (currentDate.daysTo(underDate) > 0) {
|
|
||||||
drawDateDevider(option.rect.bottom(), underDate, painter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
QRect stripe = option.rect;
|
|
||||||
stripe.setLeft(0);
|
|
||||||
stripe.setWidth(viewportRect.width());
|
|
||||||
bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor);
|
|
||||||
option.state.setFlag(QStyle::State_MouseOver, mouseOver);
|
option.state.setFlag(QStyle::State_MouseOver, mouseOver);
|
||||||
itemDelegate(index)->paint(&painter, option, index);
|
itemDelegate(index)->paint(&painter, option, index);
|
||||||
|
|
||||||
if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) {
|
|
||||||
drawDateDevider(option.rect.bottom(), lastDate, painter);
|
|
||||||
}
|
|
||||||
lastDate = currentDate;
|
|
||||||
}
|
|
||||||
if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space
|
|
||||||
drawDateDevider(option.rect.top() - dateDeviderMargin * 2 - dividerMetrics.height(), lastDate, painter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clearWidgetsMode && specialDelegate) {
|
if (clearWidgetsMode && specialDelegate) {
|
||||||
@ -386,16 +294,10 @@ void FeedView::paintEvent(QPaintEvent* event)
|
|||||||
del->endClearWidgets();
|
del->endClearWidgets();
|
||||||
clearWidgetsMode = false;
|
clearWidgetsMode = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (event->rect().height() == vp->height()) {
|
||||||
void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter)
|
// draw the blurred drop shadow...
|
||||||
{
|
}
|
||||||
int divisionHeight = dateDeviderMargin * 2 + dividerMetrics.height();
|
|
||||||
QRect r(QPoint(0, top), QSize(viewport()->width(), divisionHeight));
|
|
||||||
painter.save();
|
|
||||||
painter.setFont(dividerFont);
|
|
||||||
painter.drawText(r, Qt::AlignCenter, date.toString("d MMMM"));
|
|
||||||
painter.restore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::verticalScrollbarValueChanged(int value)
|
void FeedView::verticalScrollbarValueChanged(int value)
|
||||||
@ -415,151 +317,13 @@ void FeedView::verticalScrollbarValueChanged(int value)
|
|||||||
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::setAnchorHovered(Shared::Hover type)
|
|
||||||
{
|
|
||||||
if (hovered != type) {
|
|
||||||
hovered = type;
|
|
||||||
switch (hovered) {
|
|
||||||
case Shared::Hover::nothing:
|
|
||||||
setCursor(Qt::ArrowCursor);
|
|
||||||
break;
|
|
||||||
case Shared::Hover::text:
|
|
||||||
setCursor(Qt::IBeamCursor);
|
|
||||||
break;
|
|
||||||
case Shared::Hover::anchor:
|
|
||||||
setCursor(Qt::PointingHandCursor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dragEndPoint = event->localPos().toPoint();
|
|
||||||
if (mousePressed) {
|
|
||||||
QPoint distance = dragStartPoint - dragEndPoint;
|
|
||||||
if (distance.manhattanLength() > 5) {
|
|
||||||
dragging = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractItemView::mouseMoveEvent(event);
|
QAbstractItemView::mouseMoveEvent(event);
|
||||||
|
|
||||||
if (specialDelegate) {
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
if (dragging) {
|
|
||||||
QModelIndex index = indexAt(dragStartPoint);
|
|
||||||
if (index.isValid()) {
|
|
||||||
QRect rect = visualRect(index);
|
|
||||||
if (rect.contains(dragStartPoint)) {
|
|
||||||
QString selected = del->mouseDrag(dragStartPoint, dragEndPoint, index, rect);
|
|
||||||
if (selectedText != selected) {
|
|
||||||
selectedText = selected;
|
|
||||||
setDirtyRegion(rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QModelIndex index = indexAt(dragEndPoint);
|
|
||||||
if (index.isValid()) {
|
|
||||||
QRect rect = visualRect(index);
|
|
||||||
if (rect.contains(dragEndPoint)) {
|
|
||||||
setAnchorHovered(del->hoverType(dragEndPoint, index, rect));
|
|
||||||
} else {
|
|
||||||
setAnchorHovered(Shared::Hover::nothing);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setAnchorHovered(Shared::Hover::nothing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::mousePressEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
QAbstractItemView::mousePressEvent(event);
|
|
||||||
|
|
||||||
mousePressed = event->button() == Qt::LeftButton;
|
|
||||||
if (mousePressed) {
|
|
||||||
dragStartPoint = event->localPos().toPoint();
|
|
||||||
if (specialDelegate && specialModel) {
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
QString lastSelectedId = del->clearSelection();
|
|
||||||
if (lastSelectedId.size()) {
|
|
||||||
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
|
||||||
QModelIndex index = feed->modelIndexById(lastSelectedId);
|
|
||||||
if (index.isValid()) {
|
|
||||||
setDirtyRegion(visualRect(index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::mouseDoubleClickEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
QAbstractItemView::mouseDoubleClickEvent(event);
|
|
||||||
mousePressed = event->button() == Qt::LeftButton;
|
|
||||||
if (mousePressed) {
|
|
||||||
dragStartPoint = event->localPos().toPoint();
|
|
||||||
if (specialDelegate && specialModel) {
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
QString lastSelectedId = del->clearSelection();
|
|
||||||
selectedText = "";
|
|
||||||
if (lastSelectedId.size()) {
|
|
||||||
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
|
||||||
QModelIndex index = feed->modelIndexById(lastSelectedId);
|
|
||||||
if (index.isValid()) {
|
|
||||||
setDirtyRegion(visualRect(index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex index = indexAt(dragStartPoint);
|
|
||||||
QRect rect = visualRect(index);
|
|
||||||
if (rect.contains(dragStartPoint)) {
|
|
||||||
selectedText = del->leftDoubleClick(dragStartPoint, index, rect);
|
|
||||||
if (selectedText.size() > 0) {
|
|
||||||
setDirtyRegion(rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::mouseReleaseEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
QAbstractItemView::mouseReleaseEvent(event);
|
|
||||||
|
|
||||||
if (mousePressed) {
|
|
||||||
if (!dragging && specialDelegate) {
|
|
||||||
QPoint point = event->localPos().toPoint();
|
|
||||||
QModelIndex index = indexAt(point);
|
|
||||||
if (index.isValid()) {
|
|
||||||
QRect rect = visualRect(index);
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
if (rect.contains(point)) {
|
|
||||||
del->leftClick(point, index, rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dragging = false;
|
|
||||||
mousePressed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::keyPressEvent(QKeyEvent* event)
|
|
||||||
{
|
|
||||||
QKeyEvent *key_event = static_cast<QKeyEvent*>(event);
|
|
||||||
if (key_event->matches(QKeySequence::Copy)) {
|
|
||||||
if (selectedText.size() > 0) {
|
|
||||||
QClipboard* cb = QApplication::clipboard();
|
|
||||||
cb->setText(selectedText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::resizeEvent(QResizeEvent* event)
|
void FeedView::resizeEvent(QResizeEvent* event)
|
||||||
@ -603,13 +367,10 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
|
|||||||
MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate);
|
MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate);
|
||||||
if (del) {
|
if (del) {
|
||||||
specialDelegate = true;
|
specialDelegate = true;
|
||||||
elementMargin = MessageDelegate::margin;
|
|
||||||
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
||||||
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
||||||
connect(del, &MessageDelegate::openLink, &QDesktopServices::openUrl);
|
|
||||||
} else {
|
} else {
|
||||||
specialDelegate = false;
|
specialDelegate = false;
|
||||||
elementMargin = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,11 +394,16 @@ void FeedView::setModel(QAbstractItemModel* p_model)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::onMessageButtonPushed(const QString& messageId)
|
void FeedView::onMessageButtonPushed(const QString& messageId, bool download)
|
||||||
{
|
{
|
||||||
if (specialModel) {
|
if (specialModel) {
|
||||||
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
||||||
feed->downloadAttachment(messageId);
|
|
||||||
|
if (download) {
|
||||||
|
feed->downloadAttachment(messageId);
|
||||||
|
} else {
|
||||||
|
feed->uploadAttachment(messageId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -671,8 +437,3 @@ void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state)
|
|||||||
scheduleDelayedItemsLayout();
|
scheduleDelayedItemsLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FeedView::getSelectedText() const
|
|
||||||
{
|
|
||||||
return selectedText;
|
|
||||||
}
|
|
@ -20,15 +20,12 @@
|
|||||||
#define FEEDVIEW_H
|
#define FEEDVIEW_H
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <ui/widgets/messageline/messagefeed.h>
|
#include <ui/models/messagefeed.h>
|
||||||
#include <ui/utils/progress.h>
|
#include "progress.h"
|
||||||
#include <shared/utils.h>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
@ -51,7 +48,6 @@ public:
|
|||||||
void setModel(QAbstractItemModel * model) override;
|
void setModel(QAbstractItemModel * model) override;
|
||||||
|
|
||||||
QFont getFont() const;
|
QFont getFont() const;
|
||||||
QString getSelectedText() const;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resized();
|
void resized();
|
||||||
@ -62,7 +58,7 @@ protected slots:
|
|||||||
void rowsInserted(const QModelIndex & parent, int start, int end) override;
|
void rowsInserted(const QModelIndex & parent, int start, int end) override;
|
||||||
void verticalScrollbarValueChanged(int value) override;
|
void verticalScrollbarValueChanged(int value) override;
|
||||||
void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles) override;
|
void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles) override;
|
||||||
void onMessageButtonPushed(const QString& messageId);
|
void onMessageButtonPushed(const QString& messageId, bool download);
|
||||||
void onMessageInvalidPath(const QString& messageId);
|
void onMessageInvalidPath(const QString& messageId);
|
||||||
void onModelSyncStateChange(Models::MessageFeed::SyncState state);
|
void onModelSyncStateChange(Models::MessageFeed::SyncState state);
|
||||||
|
|
||||||
@ -72,42 +68,25 @@ protected:
|
|||||||
void paintEvent(QPaintEvent * event) override;
|
void paintEvent(QPaintEvent * event) override;
|
||||||
void updateGeometries() override;
|
void updateGeometries() override;
|
||||||
void mouseMoveEvent(QMouseEvent * event) override;
|
void mouseMoveEvent(QMouseEvent * event) override;
|
||||||
void mousePressEvent(QMouseEvent * event) override;
|
|
||||||
void mouseReleaseEvent(QMouseEvent * event) override;
|
|
||||||
void mouseDoubleClickEvent(QMouseEvent * event) override;
|
|
||||||
void keyPressEvent(QKeyEvent * event) override;
|
|
||||||
void resizeEvent(QResizeEvent * event) override;
|
void resizeEvent(QResizeEvent * event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
||||||
void positionProgress();
|
void positionProgress();
|
||||||
void drawDateDevider(int top, const QDateTime& date, QPainter& painter);
|
|
||||||
void setAnchorHovered(Shared::Hover type);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Hint {
|
struct Hint {
|
||||||
bool dirty;
|
bool dirty;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
uint32_t width;
|
|
||||||
uint32_t x;
|
|
||||||
};
|
};
|
||||||
std::deque<Hint> hints;
|
std::deque<Hint> hints;
|
||||||
int vo;
|
int vo;
|
||||||
int elementMargin;
|
|
||||||
bool specialDelegate;
|
bool specialDelegate;
|
||||||
bool specialModel;
|
bool specialModel;
|
||||||
bool clearWidgetsMode;
|
bool clearWidgetsMode;
|
||||||
Models::MessageFeed::SyncState modelState;
|
Models::MessageFeed::SyncState modelState;
|
||||||
Progress progress;
|
Progress progress;
|
||||||
QFont dividerFont;
|
|
||||||
QFontMetrics dividerMetrics;
|
|
||||||
bool mousePressed;
|
|
||||||
bool dragging;
|
|
||||||
Shared::Hover hovered;
|
|
||||||
QPoint dragStartPoint;
|
|
||||||
QPoint dragEndPoint;
|
|
||||||
QString selectedText;
|
|
||||||
|
|
||||||
static const std::set<int> geometryChangingRoles;
|
static const std::set<int> geometryChangingRoles;
|
||||||
|
|
344
ui/utils/message.cpp
Normal file
344
ui/utils/message.cpp
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "message.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMimeDatabase>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
Message::Message(const Shared::Message& source, bool p_outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent):
|
||||||
|
QWidget(parent),
|
||||||
|
outgoing(p_outgoing),
|
||||||
|
msg(source),
|
||||||
|
body(new QWidget()),
|
||||||
|
statusBar(new QWidget()),
|
||||||
|
bodyLayout(new QVBoxLayout(body)),
|
||||||
|
layout(new QHBoxLayout(this)),
|
||||||
|
date(new QLabel(msg.getTime().toLocalTime().toString())),
|
||||||
|
sender(new QLabel(p_sender)),
|
||||||
|
text(new QLabel()),
|
||||||
|
shadow(new QGraphicsDropShadowEffect()),
|
||||||
|
button(0),
|
||||||
|
file(0),
|
||||||
|
progress(0),
|
||||||
|
fileComment(new QLabel()),
|
||||||
|
statusIcon(0),
|
||||||
|
editedLabel(0),
|
||||||
|
avatar(new Image(avatarPath.size() == 0 ? Shared::iconPath("user", true) : avatarPath, 60)),
|
||||||
|
hasButton(false),
|
||||||
|
hasProgress(false),
|
||||||
|
hasFile(false),
|
||||||
|
commentAdded(false),
|
||||||
|
hasStatusIcon(false),
|
||||||
|
hasEditedLabel(false)
|
||||||
|
{
|
||||||
|
setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->setContentsMargins(10, 5, 10, 5);
|
||||||
|
body->setBackgroundRole(QPalette::AlternateBase);
|
||||||
|
body->setAutoFillBackground(true);
|
||||||
|
|
||||||
|
QString bd = Shared::processMessageBody(msg.getBody());
|
||||||
|
text->setTextFormat(Qt::RichText);
|
||||||
|
text->setText(bd);;
|
||||||
|
text->setTextInteractionFlags(text->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
||||||
|
text->setWordWrap(true);
|
||||||
|
text->setOpenExternalLinks(true);
|
||||||
|
if (bd.size() == 0) {
|
||||||
|
text->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
QFont dFont = date->font();
|
||||||
|
dFont.setItalic(true);
|
||||||
|
dFont.setPointSize(dFont.pointSize() - 2);
|
||||||
|
date->setFont(dFont);
|
||||||
|
|
||||||
|
QFont f;
|
||||||
|
f.setBold(true);
|
||||||
|
sender->setFont(f);
|
||||||
|
|
||||||
|
bodyLayout->addWidget(sender);
|
||||||
|
bodyLayout->addWidget(text);
|
||||||
|
|
||||||
|
shadow->setBlurRadius(10);
|
||||||
|
shadow->setXOffset(1);
|
||||||
|
shadow->setYOffset(1);
|
||||||
|
shadow->setColor(Qt::black);
|
||||||
|
body->setGraphicsEffect(shadow);
|
||||||
|
avatar->setMaximumHeight(60);
|
||||||
|
avatar->setMaximumWidth(60);
|
||||||
|
|
||||||
|
statusBar->setContentsMargins(0, 0, 0, 0);
|
||||||
|
QHBoxLayout* statusLay = new QHBoxLayout();
|
||||||
|
statusLay->setContentsMargins(0, 0, 0, 0);
|
||||||
|
statusBar->setLayout(statusLay);
|
||||||
|
|
||||||
|
if (outgoing) {
|
||||||
|
sender->setAlignment(Qt::AlignRight);
|
||||||
|
date->setAlignment(Qt::AlignRight);
|
||||||
|
statusIcon = new QLabel();
|
||||||
|
setState();
|
||||||
|
statusLay->addWidget(statusIcon);
|
||||||
|
statusLay->addWidget(date);
|
||||||
|
layout->addStretch();
|
||||||
|
layout->addWidget(body);
|
||||||
|
layout->addWidget(avatar);
|
||||||
|
hasStatusIcon = true;
|
||||||
|
} else {
|
||||||
|
layout->addWidget(avatar);
|
||||||
|
layout->addWidget(body);
|
||||||
|
layout->addStretch();
|
||||||
|
statusLay->addWidget(date);
|
||||||
|
}
|
||||||
|
if (msg.getEdited()) {
|
||||||
|
setEdited();
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyLayout->addWidget(statusBar);
|
||||||
|
layout->setAlignment(avatar, Qt::AlignTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::~Message()
|
||||||
|
{
|
||||||
|
if (!commentAdded) {
|
||||||
|
delete fileComment;
|
||||||
|
}
|
||||||
|
//delete body; //not sure if I should delete it here, it's probably already owned by the infrastructure and gonna die with the rest of the widget
|
||||||
|
//delete avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Message::getId() const
|
||||||
|
{
|
||||||
|
return msg.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Message::getSenderJid() const
|
||||||
|
{
|
||||||
|
return msg.getFromJid();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Message::getSenderResource() const
|
||||||
|
{
|
||||||
|
return msg.getFromResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Message::getFileUrl() const
|
||||||
|
{
|
||||||
|
return msg.getOutOfBandUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::setSender(const QString& p_sender)
|
||||||
|
{
|
||||||
|
sender->setText(p_sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip)
|
||||||
|
{
|
||||||
|
hideFile();
|
||||||
|
hideProgress();
|
||||||
|
if (!hasButton) {
|
||||||
|
hideComment();
|
||||||
|
if (msg.getBody() == msg.getOutOfBandUrl()) {
|
||||||
|
text->setText("");
|
||||||
|
text->hide();
|
||||||
|
}
|
||||||
|
button = new QPushButton(icon, buttonText);
|
||||||
|
button->setToolTip(tooltip);
|
||||||
|
connect(button, &QPushButton::clicked, this, &Message::buttonClicked);
|
||||||
|
bodyLayout->insertWidget(2, button);
|
||||||
|
hasButton = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::setProgress(qreal value)
|
||||||
|
{
|
||||||
|
hideFile();
|
||||||
|
hideButton();
|
||||||
|
if (!hasProgress) {
|
||||||
|
hideComment();
|
||||||
|
if (msg.getBody() == msg.getOutOfBandUrl()) {
|
||||||
|
text->setText("");
|
||||||
|
text->hide();
|
||||||
|
}
|
||||||
|
progress = new QProgressBar();
|
||||||
|
progress->setRange(0, 100);
|
||||||
|
bodyLayout->insertWidget(2, progress);
|
||||||
|
hasProgress = true;
|
||||||
|
}
|
||||||
|
progress->setValue(value * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::showFile(const QString& path)
|
||||||
|
{
|
||||||
|
hideButton();
|
||||||
|
hideProgress();
|
||||||
|
if (!hasFile) {
|
||||||
|
hideComment();
|
||||||
|
if (msg.getBody() == msg.getOutOfBandUrl()) {
|
||||||
|
text->setText("");
|
||||||
|
text->hide();
|
||||||
|
}
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType type = db.mimeTypeForFile(path);
|
||||||
|
QStringList parts = type.name().split("/");
|
||||||
|
QString big = parts.front();
|
||||||
|
QFileInfo info(path);
|
||||||
|
if (big == "image") {
|
||||||
|
file = new Image(path);
|
||||||
|
} else {
|
||||||
|
file = new QLabel();
|
||||||
|
file->setPixmap(QIcon::fromTheme(type.iconName()).pixmap(50));
|
||||||
|
file->setAlignment(Qt::AlignCenter);
|
||||||
|
showComment(info.fileName(), true);
|
||||||
|
}
|
||||||
|
file->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||||
|
QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file);
|
||||||
|
connect(openAction, &QAction::triggered, [path]() { //TODO need to get rid of this shame
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||||
|
});
|
||||||
|
file->addAction(openAction);
|
||||||
|
bodyLayout->insertWidget(2, file);
|
||||||
|
hasFile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::hideComment()
|
||||||
|
{
|
||||||
|
if (commentAdded) {
|
||||||
|
bodyLayout->removeWidget(fileComment);
|
||||||
|
fileComment->hide();
|
||||||
|
fileComment->setWordWrap(false);
|
||||||
|
commentAdded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::hideButton()
|
||||||
|
{
|
||||||
|
if (hasButton) {
|
||||||
|
button->deleteLater();
|
||||||
|
button = 0;
|
||||||
|
hasButton = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::hideFile()
|
||||||
|
{
|
||||||
|
if (hasFile) {
|
||||||
|
file->deleteLater();
|
||||||
|
file = 0;
|
||||||
|
hasFile = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::hideProgress()
|
||||||
|
{
|
||||||
|
if (hasProgress) {
|
||||||
|
progress->deleteLater();
|
||||||
|
progress = 0;
|
||||||
|
hasProgress = false;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Message::showComment(const QString& comment, bool wordWrap)
|
||||||
|
{
|
||||||
|
if (!commentAdded) {
|
||||||
|
int index = 2;
|
||||||
|
if (hasFile) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (hasButton) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (hasProgress) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
bodyLayout->insertWidget(index, fileComment);
|
||||||
|
fileComment->show();
|
||||||
|
commentAdded = true;
|
||||||
|
}
|
||||||
|
fileComment->setWordWrap(wordWrap);
|
||||||
|
fileComment->setText(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Shared::Message & Message::getMessage() const
|
||||||
|
{
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::setAvatarPath(const QString& p_path)
|
||||||
|
{
|
||||||
|
if (p_path.size() == 0) {
|
||||||
|
avatar->setPath(Shared::iconPath("user", true));
|
||||||
|
} else {
|
||||||
|
avatar->setPath(p_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Message::change(const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
bool idChanged = msg.change(data);
|
||||||
|
|
||||||
|
QString body = msg.getBody();
|
||||||
|
QString bd = Shared::processMessageBody(body);
|
||||||
|
if (body.size() > 0) {
|
||||||
|
text->setText(bd);
|
||||||
|
text->show();
|
||||||
|
} else {
|
||||||
|
text->setText(body);
|
||||||
|
text->hide();
|
||||||
|
}
|
||||||
|
if (msg.getEdited()) {
|
||||||
|
setEdited();
|
||||||
|
}
|
||||||
|
if (hasStatusIcon) {
|
||||||
|
setState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return idChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::setEdited()
|
||||||
|
{
|
||||||
|
if (!hasEditedLabel) {
|
||||||
|
editedLabel = new QLabel();
|
||||||
|
hasEditedLabel = true;
|
||||||
|
QIcon q(Shared::icon("edit-rename"));
|
||||||
|
editedLabel->setPixmap(q.pixmap(12, 12));
|
||||||
|
QHBoxLayout* statusLay = static_cast<QHBoxLayout*>(statusBar->layout());
|
||||||
|
statusLay->insertWidget(1, editedLabel);
|
||||||
|
}
|
||||||
|
editedLabel->setToolTip("Last time edited: " + msg.getLastModified().toLocalTime().toString()
|
||||||
|
+ "\nOriginal message: " + msg.getOriginalBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Message::setState()
|
||||||
|
{
|
||||||
|
Shared::Message::State state = msg.getState();
|
||||||
|
QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast<uint8_t>(state)]));
|
||||||
|
QString tt = Shared::Global::getName(state);
|
||||||
|
if (state == Shared::Message::State::error) {
|
||||||
|
QString errText = msg.getErrorText();
|
||||||
|
if (errText.size() > 0) {
|
||||||
|
tt += ": " + errText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusIcon->setToolTip(tt);
|
||||||
|
statusIcon->setPixmap(q.pixmap(12, 12));
|
||||||
|
}
|
||||||
|
|
103
ui/utils/message.h
Normal file
103
ui/utils/message.h
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MESSAGE_H
|
||||||
|
#define MESSAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QGraphicsDropShadowEffect>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include "shared/message.h"
|
||||||
|
#include "shared/icons.h"
|
||||||
|
#include "shared/global.h"
|
||||||
|
#include "shared/utils.h"
|
||||||
|
#include "resizer.h"
|
||||||
|
#include "image.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class Message : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Message(const Shared::Message& source, bool outgoing, const QString& sender, const QString& avatarPath = "", QWidget* parent = nullptr);
|
||||||
|
~Message();
|
||||||
|
|
||||||
|
void setSender(const QString& sender);
|
||||||
|
QString getId() const;
|
||||||
|
QString getSenderJid() const;
|
||||||
|
QString getSenderResource() const;
|
||||||
|
QString getFileUrl() const;
|
||||||
|
const Shared::Message& getMessage() const;
|
||||||
|
|
||||||
|
void addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip = "");
|
||||||
|
void showComment(const QString& comment, bool wordWrap = false);
|
||||||
|
void hideComment();
|
||||||
|
void showFile(const QString& path);
|
||||||
|
void setProgress(qreal value);
|
||||||
|
void setAvatarPath(const QString& p_path);
|
||||||
|
bool change(const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
bool const outgoing;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void buttonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Shared::Message msg;
|
||||||
|
QWidget* body;
|
||||||
|
QWidget* statusBar;
|
||||||
|
QVBoxLayout* bodyLayout;
|
||||||
|
QHBoxLayout* layout;
|
||||||
|
QLabel* date;
|
||||||
|
QLabel* sender;
|
||||||
|
QLabel* text;
|
||||||
|
QGraphicsDropShadowEffect* shadow;
|
||||||
|
QPushButton* button;
|
||||||
|
QLabel* file;
|
||||||
|
QProgressBar* progress;
|
||||||
|
QLabel* fileComment;
|
||||||
|
QLabel* statusIcon;
|
||||||
|
QLabel* editedLabel;
|
||||||
|
Image* avatar;
|
||||||
|
bool hasButton;
|
||||||
|
bool hasProgress;
|
||||||
|
bool hasFile;
|
||||||
|
bool commentAdded;
|
||||||
|
bool hasStatusIcon;
|
||||||
|
bool hasEditedLabel;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void hideButton();
|
||||||
|
void hideProgress();
|
||||||
|
void hideFile();
|
||||||
|
void setState();
|
||||||
|
void setEdited();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MESSAGE_H
|
549
ui/utils/messagedelegate.cpp
Normal file
549
ui/utils/messagedelegate.cpp
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
#include "messagedelegate.h"
|
||||||
|
#include "ui/models/messagefeed.h"
|
||||||
|
|
||||||
|
constexpr int avatarHeight = 50;
|
||||||
|
constexpr int margin = 6;
|
||||||
|
constexpr int textMargin = 2;
|
||||||
|
constexpr int statusIconSize = 16;
|
||||||
|
constexpr int maxAttachmentHeight = 500;
|
||||||
|
|
||||||
|
MessageDelegate::MessageDelegate(QObject* parent):
|
||||||
|
QStyledItemDelegate(parent),
|
||||||
|
bodyFont(),
|
||||||
|
nickFont(),
|
||||||
|
dateFont(),
|
||||||
|
bodyMetrics(bodyFont),
|
||||||
|
nickMetrics(nickFont),
|
||||||
|
dateMetrics(dateFont),
|
||||||
|
buttonHeight(0),
|
||||||
|
barHeight(0),
|
||||||
|
buttons(new std::map<QString, FeedButton*>()),
|
||||||
|
bars(new std::map<QString, QProgressBar*>()),
|
||||||
|
statusIcons(new std::map<QString, QLabel*>()),
|
||||||
|
bodies(new std::map<QString, QLabel*>()),
|
||||||
|
idsToKeep(new std::set<QString>()),
|
||||||
|
clearingWidgets(false)
|
||||||
|
{
|
||||||
|
QPushButton btn;
|
||||||
|
buttonHeight = btn.sizeHint().height();
|
||||||
|
|
||||||
|
QProgressBar bar;
|
||||||
|
barHeight = bar.sizeHint().height();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDelegate::~MessageDelegate()
|
||||||
|
{
|
||||||
|
for (const std::pair<const QString, FeedButton*>& pair: *buttons){
|
||||||
|
delete pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::pair<const QString, QProgressBar*>& pair: *bars){
|
||||||
|
delete pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::pair<const QString, QLabel*>& pair: *statusIcons){
|
||||||
|
delete pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::pair<const QString, QLabel*>& pair: *bodies){
|
||||||
|
delete pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete idsToKeep;
|
||||||
|
delete buttons;
|
||||||
|
delete bars;
|
||||||
|
delete bodies;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||||
|
if (!vi.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
|
painter->save();
|
||||||
|
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
|
||||||
|
if (option.state & QStyle::State_MouseOver) {
|
||||||
|
painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon icon(data.avatar);
|
||||||
|
|
||||||
|
if (data.sentByMe) {
|
||||||
|
painter->drawPixmap(option.rect.width() - avatarHeight - margin, option.rect.y() + margin / 2, icon.pixmap(avatarHeight, avatarHeight));
|
||||||
|
} else {
|
||||||
|
painter->drawPixmap(margin, option.rect.y() + margin / 2, icon.pixmap(avatarHeight, avatarHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
QStyleOptionViewItem opt = option;
|
||||||
|
QRect messageRect = option.rect.adjusted(margin, margin / 2, -(avatarHeight + 2 * margin), -margin / 2);
|
||||||
|
if (!data.sentByMe) {
|
||||||
|
opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop;
|
||||||
|
messageRect.adjust(avatarHeight + margin, 0, avatarHeight + margin, 0);
|
||||||
|
} else {
|
||||||
|
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
||||||
|
}
|
||||||
|
opt.rect = messageRect;
|
||||||
|
|
||||||
|
QSize messageSize(0, 0);
|
||||||
|
QSize bodySize(0, 0);
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size();
|
||||||
|
bodySize = messageSize;
|
||||||
|
}
|
||||||
|
messageSize.rheight() += nickMetrics.lineSpacing();
|
||||||
|
messageSize.rheight() += dateMetrics.height();
|
||||||
|
if (messageSize.width() < opt.rect.width()) {
|
||||||
|
QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size();
|
||||||
|
if (senderSize.width() > messageSize.width()) {
|
||||||
|
messageSize.setWidth(senderSize.width());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
messageSize.setWidth(opt.rect.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect rect;
|
||||||
|
painter->setFont(nickFont);
|
||||||
|
painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect);
|
||||||
|
opt.rect.adjust(0, rect.height() + textMargin, 0, 0);
|
||||||
|
painter->save();
|
||||||
|
switch (data.attach.state) {
|
||||||
|
case Models::none:
|
||||||
|
clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed
|
||||||
|
break; //but it's a possible performance problem
|
||||||
|
case Models::uploading:
|
||||||
|
case Models::downloading:
|
||||||
|
paintBar(getBar(data), painter, data.sentByMe, opt);
|
||||||
|
break;
|
||||||
|
case Models::remote:
|
||||||
|
case Models::local:
|
||||||
|
paintButton(getButton(data), painter, data.sentByMe, opt);
|
||||||
|
break;
|
||||||
|
case Models::ready:
|
||||||
|
clearHelperWidget(data);
|
||||||
|
paintPreview(data, painter, opt);
|
||||||
|
break;
|
||||||
|
case Models::errorDownload:
|
||||||
|
case Models::errorUpload:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
painter->restore();
|
||||||
|
|
||||||
|
int messageLeft = INT16_MAX;
|
||||||
|
QWidget* vp = static_cast<QWidget*>(painter->device());
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
QLabel* body = getBody(data);
|
||||||
|
body->setParent(vp);
|
||||||
|
body->setMaximumWidth(bodySize.width());
|
||||||
|
body->setMinimumWidth(bodySize.width());
|
||||||
|
body->setAlignment(opt.displayAlignment);
|
||||||
|
messageLeft = opt.rect.x();
|
||||||
|
if (data.sentByMe) {
|
||||||
|
messageLeft = opt.rect.topRight().x() - bodySize.width();
|
||||||
|
}
|
||||||
|
body->move(messageLeft, opt.rect.y());
|
||||||
|
body->show();
|
||||||
|
opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
|
||||||
|
}
|
||||||
|
painter->setFont(dateFont);
|
||||||
|
QColor q = painter->pen().color();
|
||||||
|
q.setAlpha(180);
|
||||||
|
painter->setPen(q);
|
||||||
|
painter->drawText(opt.rect, opt.displayAlignment, data.date.toLocalTime().toString(), &rect);
|
||||||
|
if (data.sentByMe) {
|
||||||
|
if (messageLeft > rect.x() - statusIconSize - margin) {
|
||||||
|
messageLeft = rect.x() - statusIconSize - margin;
|
||||||
|
}
|
||||||
|
QLabel* statusIcon = getStatusIcon(data);
|
||||||
|
|
||||||
|
statusIcon->setParent(vp);
|
||||||
|
statusIcon->move(messageLeft, opt.rect.y());
|
||||||
|
statusIcon->show();
|
||||||
|
opt.rect.adjust(0, statusIconSize + textMargin, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
|
|
||||||
|
if (clearingWidgets) {
|
||||||
|
idsToKeep->insert(data.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
QRect messageRect = option.rect.adjusted(0, margin / 2, -(avatarHeight + 3 * margin), -margin / 2);
|
||||||
|
QStyleOptionViewItem opt = option;
|
||||||
|
opt.rect = messageRect;
|
||||||
|
QVariant va = index.data(Models::MessageFeed::Attach);
|
||||||
|
Models::Attachment attach = qvariant_cast<Models::Attachment>(va);
|
||||||
|
QString body = index.data(Models::MessageFeed::Text).toString();
|
||||||
|
QSize messageSize(0, 0);
|
||||||
|
if (body.size() > 0) {
|
||||||
|
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, body).size();
|
||||||
|
messageSize.rheight() += textMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (attach.state) {
|
||||||
|
case Models::none:
|
||||||
|
break;
|
||||||
|
case Models::uploading:
|
||||||
|
case Models::downloading:
|
||||||
|
messageSize.rheight() += barHeight + textMargin;
|
||||||
|
break;
|
||||||
|
case Models::remote:
|
||||||
|
case Models::local:
|
||||||
|
messageSize.rheight() += buttonHeight + textMargin;
|
||||||
|
break;
|
||||||
|
case Models::ready:
|
||||||
|
messageSize.rheight() += calculateAttachSize(attach.localPath, messageRect).height() + textMargin;
|
||||||
|
break;
|
||||||
|
case Models::errorDownload:
|
||||||
|
case Models::errorUpload:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageSize.rheight() += nickMetrics.lineSpacing();
|
||||||
|
messageSize.rheight() += textMargin;
|
||||||
|
messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize;
|
||||||
|
|
||||||
|
if (messageSize.height() < avatarHeight) {
|
||||||
|
messageSize.setHeight(avatarHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageSize.rheight() += margin;
|
||||||
|
|
||||||
|
return messageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::initializeFonts(const QFont& font)
|
||||||
|
{
|
||||||
|
bodyFont = font;
|
||||||
|
nickFont = font;
|
||||||
|
dateFont = font;
|
||||||
|
|
||||||
|
nickFont.setBold(true);
|
||||||
|
|
||||||
|
float ndps = nickFont.pointSizeF();
|
||||||
|
if (ndps != -1) {
|
||||||
|
nickFont.setPointSizeF(ndps * 1.2);
|
||||||
|
} else {
|
||||||
|
nickFont.setPointSize(nickFont.pointSize() + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
dateFont.setItalic(true);
|
||||||
|
float dps = dateFont.pointSizeF();
|
||||||
|
if (dps != -1) {
|
||||||
|
dateFont.setPointSizeF(dps * 0.8);
|
||||||
|
} else {
|
||||||
|
dateFont.setPointSize(dateFont.pointSize() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMetrics = QFontMetrics(bodyFont);
|
||||||
|
nickMetrics = QFontMetrics(nickFont);
|
||||||
|
dateMetrics = QFontMetrics(dateFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
|
||||||
|
{
|
||||||
|
//qDebug() << event->type();
|
||||||
|
|
||||||
|
|
||||||
|
return QStyledItemDelegate::editorEvent(event, model, option, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
|
||||||
|
{
|
||||||
|
QPoint start;
|
||||||
|
if (sentByMe) {
|
||||||
|
start = {option.rect.width() - btn->width(), option.rect.top()};
|
||||||
|
} else {
|
||||||
|
start = option.rect.topLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* vp = static_cast<QWidget*>(painter->device());
|
||||||
|
btn->setParent(vp);
|
||||||
|
btn->move(start);
|
||||||
|
btn->show();
|
||||||
|
|
||||||
|
option.rect.adjust(0, buttonHeight + textMargin, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
|
||||||
|
{
|
||||||
|
QPoint start = option.rect.topLeft();
|
||||||
|
bar->resize(option.rect.width(), barHeight);
|
||||||
|
|
||||||
|
painter->translate(start);
|
||||||
|
bar->render(painter, QPoint(), QRegion(), QWidget::DrawChildren);
|
||||||
|
|
||||||
|
option.rect.adjust(0, barHeight + textMargin, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const
|
||||||
|
{
|
||||||
|
Shared::Global::FileInfo info = Shared::Global::getFileInfo(data.attach.localPath);
|
||||||
|
if (info.preview == Shared::Global::FileInfo::Preview::picture) {
|
||||||
|
QSize size = constrainAttachSize(info.size, option.rect.size());
|
||||||
|
|
||||||
|
QPoint start;
|
||||||
|
if (data.sentByMe) {
|
||||||
|
start = {option.rect.width() - size.width(), option.rect.top()};
|
||||||
|
start.rx() += margin;
|
||||||
|
} else {
|
||||||
|
start = option.rect.topLeft();
|
||||||
|
}
|
||||||
|
QImage img(data.attach.localPath);
|
||||||
|
if (img.isNull()) {
|
||||||
|
emit invalidPath(data.id);
|
||||||
|
} else {
|
||||||
|
painter->drawImage(QRect(start, size), img);
|
||||||
|
}
|
||||||
|
|
||||||
|
option.rect.adjust(0, size.height() + textMargin, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const
|
||||||
|
{
|
||||||
|
std::map<QString, FeedButton*>::const_iterator itr = buttons->find(data.id);
|
||||||
|
FeedButton* result = 0;
|
||||||
|
if (itr != buttons->end()) {
|
||||||
|
if (
|
||||||
|
(data.attach.state == Models::remote && itr->second->download) ||
|
||||||
|
(data.attach.state == Models::local && !itr->second->download)
|
||||||
|
) {
|
||||||
|
result = itr->second;
|
||||||
|
} else {
|
||||||
|
delete itr->second;
|
||||||
|
buttons->erase(itr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::map<QString, QProgressBar*>::const_iterator barItr = bars->find(data.id);
|
||||||
|
if (barItr != bars->end()) {
|
||||||
|
delete barItr->second;
|
||||||
|
bars->erase(barItr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
result = new FeedButton();
|
||||||
|
result->messageId = data.id;
|
||||||
|
if (data.attach.state == Models::remote) {
|
||||||
|
result->setText(QCoreApplication::translate("MessageLine", "Download"));
|
||||||
|
result->download = true;
|
||||||
|
} else {
|
||||||
|
result->setText(QCoreApplication::translate("MessageLine", "Upload"));
|
||||||
|
result->download = false;
|
||||||
|
}
|
||||||
|
buttons->insert(std::make_pair(data.id, result));
|
||||||
|
connect(result, &QPushButton::clicked, this, &MessageDelegate::onButtonPushed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const
|
||||||
|
{
|
||||||
|
std::map<QString, QProgressBar*>::const_iterator barItr = bars->find(data.id);
|
||||||
|
QProgressBar* result = 0;
|
||||||
|
if (barItr != bars->end()) {
|
||||||
|
result = barItr->second;
|
||||||
|
} else {
|
||||||
|
std::map<QString, FeedButton*>::const_iterator itr = buttons->find(data.id);
|
||||||
|
if (itr != buttons->end()) {
|
||||||
|
delete itr->second;
|
||||||
|
buttons->erase(itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
result = new QProgressBar();
|
||||||
|
result->setRange(0, 100);
|
||||||
|
bars->insert(std::make_pair(data.id, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
result->setValue(data.attach.progress * 100);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const
|
||||||
|
{
|
||||||
|
std::map<QString, QLabel*>::const_iterator itr = statusIcons->find(data.id);
|
||||||
|
QLabel* result = 0;
|
||||||
|
|
||||||
|
QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast<uint8_t>(data.state)]));
|
||||||
|
QString tt = Shared::Global::getName(data.state);
|
||||||
|
if (data.state == Shared::Message::State::error) {
|
||||||
|
if (data.error > 0) {
|
||||||
|
tt += ": " + data.error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itr != statusIcons->end()) {
|
||||||
|
result = itr->second;
|
||||||
|
if (result->toolTip() != tt) { //If i just assign pixmap every time unconditionally
|
||||||
|
result->setPixmap(q.pixmap(statusIconSize)); //it involves into an infinite cycle of repaint
|
||||||
|
result->setToolTip(tt); //may be it's better to subclass and store last condition in int?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = new QLabel();
|
||||||
|
statusIcons->insert(std::make_pair(data.id, result));
|
||||||
|
result->setPixmap(q.pixmap(statusIconSize));
|
||||||
|
result->setToolTip(tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
result->setToolTip(tt);
|
||||||
|
//result->setText(std::to_string((int)data.state).c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel * MessageDelegate::getBody(const Models::FeedItem& data) const
|
||||||
|
{
|
||||||
|
std::map<QString, QLabel*>::const_iterator itr = bodies->find(data.id);
|
||||||
|
QLabel* result = 0;
|
||||||
|
|
||||||
|
if (itr != bodies->end()) {
|
||||||
|
result = itr->second;
|
||||||
|
} else {
|
||||||
|
result = new QLabel();
|
||||||
|
result->setFont(bodyFont);
|
||||||
|
result->setWordWrap(true);
|
||||||
|
result->setOpenExternalLinks(true);
|
||||||
|
result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
||||||
|
bodies->insert(std::make_pair(data.id, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
result->setText(Shared::processMessageBody(data.text));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::beginClearWidgets()
|
||||||
|
{
|
||||||
|
idsToKeep->clear();
|
||||||
|
clearingWidgets = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::endClearWidgets()
|
||||||
|
{
|
||||||
|
if (clearingWidgets) {
|
||||||
|
std::set<QString> toRemoveButtons;
|
||||||
|
std::set<QString> toRemoveBars;
|
||||||
|
std::set<QString> toRemoveIcons;
|
||||||
|
std::set<QString> toRemoveBodies;
|
||||||
|
for (const std::pair<const QString, FeedButton*>& pair: *buttons) {
|
||||||
|
if (idsToKeep->find(pair.first) == idsToKeep->end()) {
|
||||||
|
delete pair.second;
|
||||||
|
toRemoveButtons.insert(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::pair<const QString, QProgressBar*>& pair: *bars) {
|
||||||
|
if (idsToKeep->find(pair.first) == idsToKeep->end()) {
|
||||||
|
delete pair.second;
|
||||||
|
toRemoveBars.insert(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::pair<const QString, QLabel*>& pair: *statusIcons) {
|
||||||
|
if (idsToKeep->find(pair.first) == idsToKeep->end()) {
|
||||||
|
delete pair.second;
|
||||||
|
toRemoveIcons.insert(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::pair<const QString, QLabel*>& pair: *bodies) {
|
||||||
|
if (idsToKeep->find(pair.first) == idsToKeep->end()) {
|
||||||
|
delete pair.second;
|
||||||
|
toRemoveBodies.insert(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString& key : toRemoveButtons) {
|
||||||
|
buttons->erase(key);
|
||||||
|
}
|
||||||
|
for (const QString& key : toRemoveBars) {
|
||||||
|
bars->erase(key);
|
||||||
|
}
|
||||||
|
for (const QString& key : toRemoveIcons) {
|
||||||
|
statusIcons->erase(key);
|
||||||
|
}
|
||||||
|
for (const QString& key : toRemoveBodies) {
|
||||||
|
bodies->erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
idsToKeep->clear();
|
||||||
|
clearingWidgets = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::onButtonPushed() const
|
||||||
|
{
|
||||||
|
FeedButton* btn = static_cast<FeedButton*>(sender());
|
||||||
|
emit buttonPushed(btn->messageId, btn->download);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const
|
||||||
|
{
|
||||||
|
std::map<QString, FeedButton*>::const_iterator itr = buttons->find(data.id);
|
||||||
|
if (itr != buttons->end()) {
|
||||||
|
delete itr->second;
|
||||||
|
buttons->erase(itr);
|
||||||
|
} else {
|
||||||
|
std::map<QString, QProgressBar*>::const_iterator barItr = bars->find(data.id);
|
||||||
|
if (barItr != bars->end()) {
|
||||||
|
delete barItr->second;
|
||||||
|
bars->erase(barItr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize MessageDelegate::calculateAttachSize(const QString& path, const QRect& bounds) const
|
||||||
|
{
|
||||||
|
Shared::Global::FileInfo info = Shared::Global::getFileInfo(path);
|
||||||
|
|
||||||
|
return constrainAttachSize(info.size, bounds.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize MessageDelegate::constrainAttachSize(QSize src, QSize bounds) const
|
||||||
|
{
|
||||||
|
bounds.setHeight(maxAttachmentHeight);
|
||||||
|
|
||||||
|
if (src.width() > bounds.width() || src.height() > bounds.height()) {
|
||||||
|
src.scale(bounds, Qt::KeepAspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
|
||||||
|
// {
|
||||||
|
//
|
||||||
|
// }
|
@ -29,14 +29,10 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QTextDocument>
|
|
||||||
|
|
||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
#include "shared/utils.h"
|
#include "shared/utils.h"
|
||||||
#include "shared/pathcheck.h"
|
|
||||||
|
|
||||||
#include "preview.h"
|
|
||||||
|
|
||||||
namespace Models {
|
namespace Models {
|
||||||
struct FeedItem;
|
struct FeedItem;
|
||||||
@ -57,40 +53,22 @@ public:
|
|||||||
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
|
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
|
||||||
void endClearWidgets();
|
void endClearWidgets();
|
||||||
void beginClearWidgets();
|
void beginClearWidgets();
|
||||||
void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
|
||||||
QString leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint);
|
|
||||||
Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
|
||||||
QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint);
|
|
||||||
QString clearSelection();
|
|
||||||
|
|
||||||
static int avatarHeight;
|
|
||||||
static int margin;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void buttonPushed(const QString& messageId) const;
|
void buttonPushed(const QString& messageId, bool download) const;
|
||||||
void invalidPath(const QString& messageId) const;
|
void invalidPath(const QString& messageId) const;
|
||||||
void openLink(const QString& href) const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
void paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||||
int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
void paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||||
int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
void paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||||
int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
|
||||||
int paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
|
||||||
void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const;
|
|
||||||
void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const;
|
|
||||||
|
|
||||||
QPushButton* getButton(const Models::FeedItem& data) const;
|
QPushButton* getButton(const Models::FeedItem& data) const;
|
||||||
QProgressBar* getBar(const Models::FeedItem& data) const;
|
QProgressBar* getBar(const Models::FeedItem& data) const;
|
||||||
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
||||||
QLabel* getPencilIcon(const Models::FeedItem& data) const;
|
QLabel* getBody(const Models::FeedItem& data) const;
|
||||||
void clearHelperWidget(const Models::FeedItem& data) const;
|
void clearHelperWidget(const Models::FeedItem& data) const;
|
||||||
|
QSize calculateAttachSize(const QString& path, const QRect& bounds) const;
|
||||||
bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const;
|
QSize constrainAttachSize(QSize src, QSize bounds) const;
|
||||||
bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const;
|
|
||||||
|
|
||||||
QRect getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const;
|
|
||||||
QString getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onButtonPushed() const;
|
void onButtonPushed() const;
|
||||||
@ -99,28 +77,26 @@ private:
|
|||||||
class FeedButton : public QPushButton {
|
class FeedButton : public QPushButton {
|
||||||
public:
|
public:
|
||||||
QString messageId;
|
QString messageId;
|
||||||
|
bool download;
|
||||||
};
|
};
|
||||||
|
|
||||||
QFont bodyFont;
|
QFont bodyFont;
|
||||||
QFont nickFont;
|
QFont nickFont;
|
||||||
QFont dateFont;
|
QFont dateFont;
|
||||||
QTextDocument* bodyRenderer;
|
QFontMetrics bodyMetrics;
|
||||||
QFontMetrics nickMetrics;
|
QFontMetrics nickMetrics;
|
||||||
QFontMetrics dateMetrics;
|
QFontMetrics dateMetrics;
|
||||||
|
|
||||||
int buttonHeight;
|
int buttonHeight;
|
||||||
int buttonWidth;
|
|
||||||
int barHeight;
|
int barHeight;
|
||||||
|
|
||||||
std::map<QString, FeedButton*>* buttons;
|
std::map<QString, FeedButton*>* buttons;
|
||||||
std::map<QString, QProgressBar*>* bars;
|
std::map<QString, QProgressBar*>* bars;
|
||||||
std::map<QString, QLabel*>* statusIcons;
|
std::map<QString, QLabel*>* statusIcons;
|
||||||
std::map<QString, QLabel*>* pencilIcons;
|
std::map<QString, QLabel*>* bodies;
|
||||||
std::map<QString, Preview*>* previews;
|
|
||||||
std::set<QString>* idsToKeep;
|
std::set<QString>* idsToKeep;
|
||||||
bool clearingWidgets;
|
bool clearingWidgets;
|
||||||
QString currentId;
|
|
||||||
std::pair<int, int> selection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MESSAGEDELEGATE_H
|
#endif // MESSAGEDELEGATE_H
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user