Compare commits
No commits in common. "various-fixes" and "master" have entirely different histories.
various-fi
...
master
@ -6,14 +6,10 @@
|
|||||||
- 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
|
|
||||||
|
|
||||||
### 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
|
|
||||||
- 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
|
|
||||||
- once uploaded local files don't get second time uploaded - the remote URL is reused
|
|
||||||
|
|
||||||
## Squawk 0.1.5 (Jul 29, 2020)
|
## Squawk 0.1.5 (Jul 29, 2020)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(squawk)
|
project(squawk)
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
@ -32,7 +32,6 @@ set(squawk_SRC
|
|||||||
shared/message.cpp
|
shared/message.cpp
|
||||||
shared/vcard.cpp
|
shared/vcard.cpp
|
||||||
shared/icons.cpp
|
shared/icons.cpp
|
||||||
shared/messageinfo.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(squawk_HEAD
|
set(squawk_HEAD
|
||||||
@ -45,7 +44,6 @@ set(squawk_HEAD
|
|||||||
shared/utils.h
|
shared/utils.h
|
||||||
shared/vcard.h
|
shared/vcard.h
|
||||||
shared/icons.h
|
shared/icons.h
|
||||||
shared/messageinfo.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
|
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
|
||||||
@ -66,7 +64,6 @@ qt5_add_resources(RCC resources/resources.qrc)
|
|||||||
|
|
||||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||||
option(WITH_KWALLET "Build KWallet support module" ON)
|
option(WITH_KWALLET "Build KWallet support module" ON)
|
||||||
option(WITH_KIO "Build KIO support module" ON)
|
|
||||||
|
|
||||||
if (SYSTEM_QXMPP)
|
if (SYSTEM_QXMPP)
|
||||||
find_package(QXmpp CONFIG)
|
find_package(QXmpp CONFIG)
|
||||||
@ -98,21 +95,8 @@ endif()
|
|||||||
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
|
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
|
||||||
target_link_libraries(squawk Qt5::Widgets)
|
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(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
add_subdirectory(plugins)
|
|
||||||
|
|
||||||
add_subdirectory(external/simpleCrypt)
|
add_subdirectory(external/simpleCrypt)
|
||||||
|
|
||||||
@ -120,8 +104,6 @@ target_link_libraries(squawk squawkUI)
|
|||||||
target_link_libraries(squawk squawkCORE)
|
target_link_libraries(squawk squawkCORE)
|
||||||
target_link_libraries(squawk uuid)
|
target_link_libraries(squawk uuid)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
||||||
|
|
||||||
# Install the executable
|
# Install the executable
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
- lmdb
|
- lmdb
|
||||||
- CMake 3.0 or higher
|
- CMake 3.0 or higher
|
||||||
- qxmpp 1.1.0 or higher
|
- qxmpp 1.1.0 or higher
|
||||||
- KDE Frameworks: kwallet (optional)
|
- kwallet (optional)
|
||||||
- KDE Frameworks: KIO (optional)
|
|
||||||
|
|
||||||
### Getting
|
### Getting
|
||||||
|
|
||||||
@ -68,7 +67,6 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`.
|
|||||||
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`)
|
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`)
|
||||||
- `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`)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
#This file is taken from here https://gitlab.ralph.or.at/causal-rt/causal-cpp/, it was GPLv3 license
|
|
||||||
#Thank you so much, mr. Ralph Alexander Bariz, I hope you don't mind me using your code
|
|
||||||
|
|
||||||
# Try to find LMDB headers and library.
|
|
||||||
#
|
|
||||||
# Usage of this module as follows:
|
|
||||||
#
|
|
||||||
# find_package(LMDB)
|
|
||||||
#
|
|
||||||
# Variables used by this module, they can change the default behaviour and need
|
|
||||||
# to be set before calling find_package:
|
|
||||||
#
|
|
||||||
# LMDB_ROOT_DIR Set this variable to the root installation of
|
|
||||||
# LMDB if the module has problems finding the
|
|
||||||
# proper installation path.
|
|
||||||
#
|
|
||||||
# Variables defined by this module:
|
|
||||||
#
|
|
||||||
# LMDB_FOUND System has LMDB library/headers.
|
|
||||||
# LMDB_LIBRARIES The LMDB library.
|
|
||||||
# LMDB_INCLUDE_DIRS The location of LMDB headers.
|
|
||||||
|
|
||||||
find_path(LMDB_ROOT_DIR
|
|
||||||
NAMES include/lmdb.h
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(LMDB_LIBRARIES
|
|
||||||
NAMES lmdb
|
|
||||||
HINTS ${LMDB_ROOT_DIR}/lib
|
|
||||||
)
|
|
||||||
|
|
||||||
find_path(LMDB_INCLUDE_DIRS
|
|
||||||
NAMES lmdb.h
|
|
||||||
HINTS ${LMDB_ROOT_DIR}/include
|
|
||||||
)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(LMDB DEFAULT_MSG
|
|
||||||
LMDB_LIBRARIES
|
|
||||||
LMDB_INCLUDE_DIRS
|
|
||||||
)
|
|
||||||
|
|
||||||
mark_as_advanced(
|
|
||||||
LMDB_ROOT_DIR
|
|
||||||
LMDB_LIBRARIES
|
|
||||||
LMDB_INCLUDE_DIRS
|
|
||||||
)
|
|
@ -1,15 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.3)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(squawkCORE)
|
project(squawkCORE)
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
find_package(Qt5Core CONFIG REQUIRED)
|
find_package(Qt5Core CONFIG REQUIRED)
|
||||||
find_package(Qt5Gui CONFIG REQUIRED)
|
find_package(Qt5Gui CONFIG REQUIRED)
|
||||||
find_package(Qt5Network CONFIG REQUIRED)
|
find_package(Qt5Network CONFIG REQUIRED)
|
||||||
find_package(Qt5Xml CONFIG REQUIRED)
|
find_package(Qt5Xml CONFIG REQUIRED)
|
||||||
find_package(LMDB REQUIRED)
|
|
||||||
|
|
||||||
set(squawkCORE_SRC
|
set(squawkCORE_SRC
|
||||||
squawk.cpp
|
squawk.cpp
|
||||||
@ -18,7 +15,7 @@ set(squawkCORE_SRC
|
|||||||
rosteritem.cpp
|
rosteritem.cpp
|
||||||
contact.cpp
|
contact.cpp
|
||||||
conference.cpp
|
conference.cpp
|
||||||
urlstorage.cpp
|
storage.cpp
|
||||||
networkaccess.cpp
|
networkaccess.cpp
|
||||||
adapterFuctions.cpp
|
adapterFuctions.cpp
|
||||||
handlers/messagehandler.cpp
|
handlers/messagehandler.cpp
|
||||||
@ -28,7 +25,7 @@ set(squawkCORE_SRC
|
|||||||
add_subdirectory(passwordStorageEngines)
|
add_subdirectory(passwordStorageEngines)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
# Tell CMake to create the helloworld executable
|
||||||
add_library(squawkCORE STATIC ${squawkCORE_SRC})
|
add_library(squawkCORE ${squawkCORE_SRC})
|
||||||
|
|
||||||
|
|
||||||
if(SYSTEM_QXMPP)
|
if(SYSTEM_QXMPP)
|
||||||
|
@ -84,9 +84,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived);
|
QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived);
|
||||||
QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived);
|
QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived);
|
||||||
|
|
||||||
QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete);
|
QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onFileUploaded);
|
||||||
QObject::connect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
QObject::connect(network, &NetworkAccess::uploadFileError, mh, &MessageHandler::onFileUploadError);
|
||||||
QObject::connect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
|
||||||
|
|
||||||
client.addExtension(rcpm);
|
client.addExtension(rcpm);
|
||||||
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
||||||
@ -156,9 +155,8 @@ Account::~Account()
|
|||||||
reconnectTimer->stop();
|
reconnectTimer->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::disconnect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete);
|
QObject::disconnect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onFileUploaded);
|
||||||
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
QObject::disconnect(network, &NetworkAccess::uploadFileError, mh, &MessageHandler::onFileUploadError);
|
||||||
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
|
||||||
|
|
||||||
delete mh;
|
delete mh;
|
||||||
delete rh;
|
delete rh;
|
||||||
@ -404,6 +402,9 @@ QString Core::Account::getFullJid() const {
|
|||||||
void Core::Account::sendMessage(const Shared::Message& data) {
|
void Core::Account::sendMessage(const Shared::Message& data) {
|
||||||
mh->sendMessage(data);}
|
mh->sendMessage(data);}
|
||||||
|
|
||||||
|
void Core::Account::sendMessage(const Shared::Message& data, const QString& path) {
|
||||||
|
mh->sendMessage(data, path);}
|
||||||
|
|
||||||
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)) {
|
||||||
@ -433,13 +434,13 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString&
|
|||||||
|
|
||||||
if (contact == 0) {
|
if (contact == 0) {
|
||||||
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping";
|
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping";
|
||||||
emit responseArchive(jid, std::list<Shared::Message>(), true);
|
emit responseArchive(jid, std::list<Shared::Message>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state != Shared::ConnectionState::connected) {
|
if (state != Shared::ConnectionState::connected) {
|
||||||
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the account is not online, skipping";
|
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the account is not online, skipping";
|
||||||
emit responseArchive(contact->jid, std::list<Shared::Message>(), false);
|
emit responseArchive(contact->jid, std::list<Shared::Message>());
|
||||||
}
|
}
|
||||||
|
|
||||||
contact->requestHistory(count, before);
|
contact->requestHistory(count, before);
|
||||||
@ -551,11 +552,9 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
case QXmppStanza::Error::NotAuthorized:
|
case QXmppStanza::Error::NotAuthorized:
|
||||||
errorText = "Authentication error";
|
errorText = "Authentication error";
|
||||||
break;
|
break;
|
||||||
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
|
|
||||||
case QXmppStanza::Error::PaymentRequired:
|
case QXmppStanza::Error::PaymentRequired:
|
||||||
errorText = "Payment is required";
|
errorText = "Payment is required";
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
case QXmppStanza::Error::RecipientUnavailable:
|
case QXmppStanza::Error::RecipientUnavailable:
|
||||||
errorText = "Recipient is unavailable";
|
errorText = "Recipient is unavailable";
|
||||||
break;
|
break;
|
||||||
@ -910,16 +909,3 @@ void Core::Account::handleDisconnection()
|
|||||||
ownVCardRequestInProgress = false;
|
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);}
|
|
||||||
|
@ -88,6 +88,7 @@ public:
|
|||||||
void setPasswordType(Shared::AccountPassword pt);
|
void setPasswordType(Shared::AccountPassword pt);
|
||||||
QString getFullJid() const;
|
QString getFullJid() const;
|
||||||
void sendMessage(const Shared::Message& data);
|
void sendMessage(const Shared::Message& data);
|
||||||
|
void sendMessage(const Shared::Message& data, const QString& path);
|
||||||
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);
|
||||||
@ -96,7 +97,6 @@ public:
|
|||||||
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
||||||
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
||||||
void renameContactRequest(const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& jid, const QString& newName);
|
||||||
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
|
||||||
|
|
||||||
void setRoomJoined(const QString& jid, bool joined);
|
void setRoomJoined(const QString& jid, bool joined);
|
||||||
void setRoomAutoJoin(const QString& jid, bool joined);
|
void setRoomAutoJoin(const QString& jid, bool joined);
|
||||||
@ -127,14 +127,14 @@ signals:
|
|||||||
void removePresence(const QString& jid, const QString& name);
|
void removePresence(const QString& jid, const QString& name);
|
||||||
void message(const Shared::Message& data);
|
void message(const Shared::Message& data);
|
||||||
void changeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void responseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last);
|
void responseArchive(const QString& jid, const std::list<Shared::Message>& list);
|
||||||
void error(const QString& text);
|
void error(const QString& text);
|
||||||
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
||||||
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& messageId, const QString& error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
@ -183,7 +183,6 @@ private slots:
|
|||||||
|
|
||||||
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
||||||
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
||||||
void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleDisconnection();
|
void handleDisconnection();
|
||||||
|
@ -271,8 +271,6 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
bool hadStanzaId = msg.getStanzaId().size() > 0;
|
bool hadStanzaId = msg.getStanzaId().size() > 0;
|
||||||
QDateTime oTime = msg.getTime();
|
QDateTime oTime = msg.getTime();
|
||||||
bool idChange = msg.change(data);
|
bool idChange = msg.change(data);
|
||||||
QDateTime nTime = msg.getTime();
|
|
||||||
bool orderChange = oTime != nTime;
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
MDB_val lmdbKey, lmdbData;
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
@ -282,21 +280,15 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
lmdbKey.mv_size = strId.size();
|
lmdbKey.mv_size = strId.size();
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
lmdbKey.mv_data = (char*)strId.c_str();
|
||||||
int rc;
|
int rc;
|
||||||
if (idChange || orderChange) {
|
|
||||||
if (idChange) {
|
if (idChange) {
|
||||||
rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
|
rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
|
||||||
} else {
|
|
||||||
quint64 ostamp = oTime.toMSecsSinceEpoch();
|
|
||||||
lmdbData.mv_data = (quint8*)&ostamp;
|
|
||||||
lmdbData.mv_size = 8;
|
|
||||||
rc = mdb_del(txn, order, &lmdbData, &lmdbKey);
|
|
||||||
}
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
strId = msg.getId().toStdString();
|
strId = msg.getId().toStdString();
|
||||||
lmdbKey.mv_size = strId.size();
|
lmdbKey.mv_size = strId.size();
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
lmdbKey.mv_data = (char*)strId.c_str();
|
||||||
|
|
||||||
quint64 stamp = nTime.toMSecsSinceEpoch();
|
|
||||||
|
quint64 stamp = oTime.toMSecsSinceEpoch();
|
||||||
lmdbData.mv_data = (quint8*)&stamp;
|
lmdbData.mv_data = (quint8*)&stamp;
|
||||||
lmdbData.mv_size = 8;
|
lmdbData.mv_size = 8;
|
||||||
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
||||||
@ -318,7 +310,7 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
lmdbData.mv_size = ba.size();
|
lmdbData.mv_size = ba.size();
|
||||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
lmdbData.mv_data = (uint8_t*)ba.data();
|
||||||
@ -510,9 +502,8 @@ long unsigned int Core::Archive::size() const
|
|||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
MDB_stat stat;
|
MDB_stat stat;
|
||||||
mdb_stat(txn, order, &stat);
|
mdb_stat(txn, order, &stat);
|
||||||
size_t amount = stat.ms_entries;
|
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
return amount;
|
return stat.ms_entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
||||||
@ -612,10 +603,10 @@ void Core::Archive::setFromTheBeginning(bool is)
|
|||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
bool success = setStatValue("beginning", is, txn);
|
bool success = setStatValue("beginning", is, txn);
|
||||||
if (success) {
|
if (success != 0) {
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} else {
|
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
|
} else {
|
||||||
|
mdb_txn_commit(txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ Core::MessageHandler::MessageHandler(Core::Account* account):
|
|||||||
QObject(),
|
QObject(),
|
||||||
acc(account),
|
acc(account),
|
||||||
pendingStateMessages(),
|
pendingStateMessages(),
|
||||||
|
pendingMessages(),
|
||||||
uploadingSlotsQueue()
|
uploadingSlotsQueue()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -53,7 +54,7 @@ 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);
|
pendingStateMessages.erase(itr);
|
||||||
handled = true;
|
handled = true;
|
||||||
@ -167,16 +168,14 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
id = source.id();
|
id = source.id();
|
||||||
#endif
|
#endif
|
||||||
target.setId(id);
|
target.setId(id);
|
||||||
QString messageId = target.getId();
|
if (target.getId().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();
|
|
||||||
}
|
}
|
||||||
target.setFrom(source.from());
|
target.setFrom(source.from());
|
||||||
target.setTo(source.to());
|
target.setTo(source.to());
|
||||||
target.setBody(source.body());
|
target.setBody(source.body());
|
||||||
target.setForwarded(forwarded);
|
target.setForwarded(forwarded);
|
||||||
|
target.setOutOfBandUrl(source.outOfBandUrl());
|
||||||
if (guessing) {
|
if (guessing) {
|
||||||
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
||||||
outgoing = true;
|
outgoing = true;
|
||||||
@ -190,12 +189,6 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
} else {
|
} else {
|
||||||
target.setCurrentTime();
|
target.setCurrentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString oob = source.outOfBandUrl();
|
|
||||||
if (oob.size() > 0) {
|
|
||||||
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
|
|
||||||
}
|
|
||||||
target.setOutOfBandUrl(oob);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
|
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
|
||||||
@ -239,23 +232,11 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessage(const Shared::Message& data)
|
void Core::MessageHandler::sendMessage(Shared::Message data)
|
||||||
{
|
|
||||||
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
|
||||||
prepareUpload(data);
|
|
||||||
} else {
|
|
||||||
performSending(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
QString oob = data.getOutOfBandUrl();
|
|
||||||
RosterItem* ri = acc->rh->getRosterItem(jid);
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
bool sent = false;
|
|
||||||
QMap<QString, QVariant> changes;
|
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
||||||
|
|
||||||
@ -264,16 +245,23 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage)
|
|||||||
#endif
|
#endif
|
||||||
msg.setId(id);
|
msg.setId(id);
|
||||||
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
||||||
msg.setOutOfBandUrl(oob);
|
msg.setOutOfBandUrl(data.getOutOfBandUrl());
|
||||||
msg.setReceiptRequested(true);
|
msg.setReceiptRequested(true);
|
||||||
|
|
||||||
sent = acc->client.sendPacket(msg);
|
bool sent = acc->client.sendPacket(msg);
|
||||||
|
|
||||||
if (sent) {
|
if (sent) {
|
||||||
data.setState(Shared::Message::State::sent);
|
data.setState(Shared::Message::State::sent);
|
||||||
} else {
|
} else {
|
||||||
data.setState(Shared::Message::State::error);
|
data.setState(Shared::Message::State::error);
|
||||||
data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs");
|
data.setErrorText("Couldn't send message via QXMPP library check out logs");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ri != 0) {
|
||||||
|
ri->appendMessageToArchive(data);
|
||||||
|
if (sent) {
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -281,74 +269,41 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage)
|
|||||||
data.setErrorText("You are is offline or reconnecting");
|
data.setErrorText("You are is offline or reconnecting");
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Message::State mstate = data.getState();
|
emit acc->changeMessage(jid, id, {
|
||||||
changes.insert("state", static_cast<uint>(mstate));
|
{"state", static_cast<uint>(data.getState())},
|
||||||
if (mstate == Shared::Message::State::error) {
|
{"errorText", data.getErrorText()}
|
||||||
changes.insert("errorText", data.getErrorText());
|
});
|
||||||
}
|
|
||||||
if (oob.size() > 0) {
|
|
||||||
changes.insert("outOfBandUrl", oob);
|
|
||||||
}
|
|
||||||
if (!newMessage) {
|
|
||||||
changes.insert("stamp", data.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ri != 0) {
|
|
||||||
if (newMessage) {
|
|
||||||
ri->appendMessageToArchive(data);
|
|
||||||
} else {
|
|
||||||
ri->changeMessage(id, changes);
|
|
||||||
}
|
|
||||||
if (sent) {
|
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
|
||||||
} else {
|
|
||||||
pendingStateMessages.erase(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit acc->changeMessage(jid, id, changes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::prepareUpload(const Shared::Message& data)
|
void Core::MessageHandler::sendMessage(const Shared::Message& data, const QString& path)
|
||||||
{
|
{
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
QString jid = data.getPenPalJid();
|
|
||||||
QString id = data.getId();
|
|
||||||
RosterItem* ri = acc->rh->getRosterItem(jid);
|
|
||||||
if (!ri) {
|
|
||||||
qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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);
|
sendMessageWithLocalUploadedFile(data, url);
|
||||||
} else {
|
} else {
|
||||||
if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
|
if (acc->network->isUploading(path, data.getId())) {
|
||||||
ri->appendMessageToArchive(data);
|
pendingMessages.emplace(data.getId(), data);
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
|
||||||
} else {
|
} else {
|
||||||
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);
|
uploadingSlotsQueue.emplace_back(path, data);
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
|
||||||
uploadingSlotsQueue.emplace_back(path, id);
|
|
||||||
if (uploadingSlotsQueue.size() == 1) {
|
if (uploadingSlotsQueue.size() == 1) {
|
||||||
acc->um->requestUploadSlot(file);
|
acc->um->requestUploadSlot(file);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
|
onFileUploadError(data.getId(), "Uploading file no longer exists or your system user has no permission to read it");
|
||||||
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
|
onFileUploadError(data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account");
|
||||||
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting");
|
onFileUploadError(data.getId(), "Account is offline or reconnecting");
|
||||||
qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping";
|
qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -359,12 +314,12 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
|
|||||||
if (uploadingSlotsQueue.size() == 0) {
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
|
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
|
||||||
} else {
|
} else {
|
||||||
const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
|
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front();
|
||||||
const QString& mId = pair.second;
|
const QString& mId = pair.second.getId();
|
||||||
QString palJid = pendingStateMessages.at(mId);
|
acc->network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
|
||||||
acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
|
pendingMessages.emplace(mId, pair.second);
|
||||||
|
|
||||||
uploadingSlotsQueue.pop_front();
|
uploadingSlotsQueue.pop_front();
|
||||||
|
|
||||||
if (uploadingSlotsQueue.size() > 0) {
|
if (uploadingSlotsQueue.size() > 0) {
|
||||||
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
}
|
}
|
||||||
@ -373,111 +328,44 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
|
|||||||
|
|
||||||
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
|
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
|
||||||
{
|
{
|
||||||
QString err(request.error().text());
|
|
||||||
if (uploadingSlotsQueue.size() == 0) {
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
|
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
|
||||||
qDebug() << err;
|
qDebug() << request.error().text();
|
||||||
} else {
|
} else {
|
||||||
const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
|
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front();
|
||||||
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err;
|
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << request.error().text();
|
||||||
handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
|
emit acc->uploadFileError(pair.second.getId(), "Error requesting slot to upload file: " + request.error().text());
|
||||||
|
|
||||||
uploadingSlotsQueue.pop_front();
|
|
||||||
if (uploadingSlotsQueue.size() > 0) {
|
if (uploadingSlotsQueue.size() > 0) {
|
||||||
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
}
|
}
|
||||||
|
uploadingSlotsQueue.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
void Core::MessageHandler::onFileUploaded(const QString& messageId, const QString& url)
|
||||||
{
|
{
|
||||||
QMap<QString, QVariant> cData = {
|
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId);
|
||||||
{"attachPath", path}
|
if (itr != pendingMessages.end()) {
|
||||||
};
|
sendMessageWithLocalUploadedFile(itr->second, url);
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
pendingMessages.erase(itr);
|
||||||
if (info.account == acc->getName()) {
|
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(info.jid);
|
|
||||||
if (cnt != 0) {
|
|
||||||
if (cnt->changeMessage(info.messageId, cData)) {
|
|
||||||
emit acc->changeMessage(info.jid, info.messageId, cData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up)
|
void Core::MessageHandler::onFileUploadError(const QString& messageId, const QString& errMsg)
|
||||||
{
|
{
|
||||||
if (up) {
|
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId);
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
if (itr != pendingMessages.end()) {
|
||||||
if (info.account == acc->getName()) {
|
pendingMessages.erase(itr);
|
||||||
handleUploadError(info.jid, info.messageId, text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
|
void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url)
|
||||||
{
|
|
||||||
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
|
|
||||||
pendingStateMessages.erase(jid);
|
|
||||||
requestChangeMessage(jid, messageId, {
|
|
||||||
{"state", static_cast<uint>(Shared::Message::State::error)},
|
|
||||||
{"errorText", errorText}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
|
||||||
if (info.account == acc->getName()) {
|
|
||||||
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
|
||||||
if (ri != 0) {
|
|
||||||
Shared::Message msg = ri->getMessage(info.messageId);
|
|
||||||
sendMessageWithLocalUploadedFile(msg, path, false);
|
|
||||||
} 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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage)
|
|
||||||
{
|
{
|
||||||
msg.setOutOfBandUrl(url);
|
msg.setOutOfBandUrl(url);
|
||||||
if (msg.getBody().size() == 0) { //not sure why, but most messages do that
|
if (msg.getBody().size() == 0) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
performSending(msg, newMessage);
|
sendMessage(msg);
|
||||||
//TODO removal/progress update
|
//TODO removal/progress update
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::set<QString> allowerToChangeKeys({
|
|
||||||
"attachPath",
|
|
||||||
"outOfBandUrl",
|
|
||||||
"state",
|
|
||||||
"errorText"
|
|
||||||
});
|
|
||||||
|
|
||||||
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
|
||||||
if (cnt != 0) {
|
|
||||||
bool allSupported = true;
|
|
||||||
QString unsupportedString;
|
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
|
||||||
if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
|
||||||
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
|
|
||||||
break; //because the underlying tech assumes that the change is initiated by user
|
|
||||||
} //not by system
|
|
||||||
}
|
|
||||||
if (allSupported) {
|
|
||||||
cnt->changeMessage(messageId, data);
|
|
||||||
emit acc->changeMessage(jid, messageId, data);
|
|
||||||
} else {
|
|
||||||
qDebug() << "A request to change message" << messageId << "of conversation" << jid << "with following data" << data;
|
|
||||||
qDebug() << "only limited set of dataFields are supported yet here, and" << unsupportedString << "isn't one of them, skipping";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include <QXmppHttpUploadIq.h>
|
#include <QXmppHttpUploadIq.h>
|
||||||
|
|
||||||
#include <shared/message.h>
|
#include <shared/message.h>
|
||||||
#include <shared/messageinfo.h>
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -45,7 +44,8 @@ public:
|
|||||||
MessageHandler(Account* account);
|
MessageHandler(Account* account);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void sendMessage(const Shared::Message& data);
|
void sendMessage(Shared::Message data);
|
||||||
|
void sendMessage(const Shared::Message& data, const QString& path);
|
||||||
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;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@ -55,24 +55,20 @@ public slots:
|
|||||||
void onReceiptReceived(const QString& jid, const QString& id);
|
void onReceiptReceived(const QString& jid, const QString& id);
|
||||||
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 onFileUploaded(const QString& messageId, const QString& url);
|
||||||
void onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void onFileUploadError(const QString& messageId, const QString& errMsg);
|
||||||
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);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
bool handleChatMessage(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);
|
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);
|
||||||
void performSending(Shared::Message data, bool newMessage = true);
|
|
||||||
void prepareUpload(const Shared::Message& data);
|
|
||||||
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Account* acc;
|
Account* acc;
|
||||||
std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
|
std::map<QString, QString> pendingStateMessages;
|
||||||
std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
|
std::map<QString, Shared::Message> pendingMessages;
|
||||||
|
std::deque<std::pair<QString, Shared::Message>> uploadingSlotsQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ void Core::RosterHandler::removeContactRequest(const QString& jid)
|
|||||||
void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact)
|
void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact)
|
||||||
{
|
{
|
||||||
connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory);
|
connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory);
|
||||||
connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse);
|
connect(contact, &RosterItem::historyResponse, this, &RosterHandler::onContactHistoryResponse);
|
||||||
connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged);
|
connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged);
|
||||||
connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged);
|
connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged);
|
||||||
connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard);
|
connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard);
|
||||||
@ -315,6 +315,14 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onContactHistoryResponse(const std::list<Shared::Message>& list)
|
||||||
|
{
|
||||||
|
RosterItem* contact = static_cast<RosterItem*>(sender());
|
||||||
|
|
||||||
|
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
||||||
|
emit acc->responseArchive(contact->jid, list);
|
||||||
|
}
|
||||||
|
|
||||||
Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
|
Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
|
||||||
{
|
{
|
||||||
RosterItem* item = 0;
|
RosterItem* item = 0;
|
||||||
|
@ -86,6 +86,7 @@ private slots:
|
|||||||
void onContactGroupRemoved(const QString& group);
|
void onContactGroupRemoved(const QString& group);
|
||||||
void onContactNameChanged(const QString& name);
|
void onContactNameChanged(const QString& name);
|
||||||
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
|
void onContactHistoryResponse(const std::list<Shared::Message>& list);
|
||||||
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -16,17 +16,13 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
#include <QtCore/QDir>
|
|
||||||
|
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
|
||||||
Core::NetworkAccess::NetworkAccess(QObject* parent):
|
Core::NetworkAccess::NetworkAccess(QObject* parent):
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
running(false),
|
running(false),
|
||||||
manager(0),
|
manager(0),
|
||||||
storage("fileURLStorage"),
|
files("files"),
|
||||||
downloads(),
|
downloads(),
|
||||||
uploads()
|
uploads()
|
||||||
{
|
{
|
||||||
@ -37,31 +33,60 @@ Core::NetworkAccess::~NetworkAccess()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::downladFile(const QString& url)
|
void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url)
|
||||||
{
|
{
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
if (itr != downloads.end()) {
|
if (itr != downloads.end()) {
|
||||||
qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping";
|
Transfer* dwn = itr->second;
|
||||||
|
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
|
||||||
|
if (mItr == dwn->messages.end()) {
|
||||||
|
dwn->messages.insert(messageId);
|
||||||
|
}
|
||||||
|
emit downloadFileProgress(messageId, dwn->progress);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url);
|
QString path = files.getRecord(url);
|
||||||
if (p.first.size() > 0) {
|
QFileInfo info(path);
|
||||||
QFileInfo info(p.first);
|
|
||||||
if (info.exists() && info.isFile()) {
|
if (info.exists() && info.isFile()) {
|
||||||
emit downloadFileComplete(p.second, p.first);
|
emit fileLocalPathResponse(messageId, path);
|
||||||
} else {
|
} else {
|
||||||
startDownload(p.second, url);
|
files.removeRecord(url);
|
||||||
}
|
emit fileLocalPathResponse(messageId, "");
|
||||||
} else {
|
|
||||||
startDownload(p.second, url);
|
|
||||||
}
|
}
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const Archive::NotFound& e) {
|
||||||
qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway";
|
emit fileLocalPathResponse(messageId, "");
|
||||||
storage.addFile(url);
|
|
||||||
startDownload(std::list<Shared::MessageInfo>(), url);
|
|
||||||
} catch (const Archive::Unknown& e) {
|
} catch (const Archive::Unknown& e) {
|
||||||
qDebug() << "Error requesting file path:" << e.what();
|
qDebug() << "Error requesting file path:" << e.what();
|
||||||
emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false);
|
emit fileLocalPathResponse(messageId, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url)
|
||||||
|
{
|
||||||
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
|
if (itr != downloads.end()) {
|
||||||
|
Transfer* dwn = itr->second;
|
||||||
|
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
|
||||||
|
if (mItr == dwn->messages.end()) {
|
||||||
|
dwn->messages.insert(messageId);
|
||||||
|
}
|
||||||
|
emit downloadFileProgress(messageId, dwn->progress);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
QString path = files.getRecord(url);
|
||||||
|
QFileInfo info(path);
|
||||||
|
if (info.exists() && info.isFile()) {
|
||||||
|
emit fileLocalPathResponse(messageId, path);
|
||||||
|
} else {
|
||||||
|
files.removeRecord(url);
|
||||||
|
startDownload(messageId, url);
|
||||||
|
}
|
||||||
|
} catch (const Archive::NotFound& e) {
|
||||||
|
startDownload(messageId, url);
|
||||||
|
} catch (const Archive::Unknown& e) {
|
||||||
|
qDebug() << "Error requesting file path:" << e.what();
|
||||||
|
emit downloadFileError(messageId, QString("Database error: ") + e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +95,7 @@ void Core::NetworkAccess::start()
|
|||||||
{
|
{
|
||||||
if (!running) {
|
if (!running) {
|
||||||
manager = new QNetworkAccessManager();
|
manager = new QNetworkAccessManager();
|
||||||
storage.open();
|
files.open();
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,7 +103,7 @@ void Core::NetworkAccess::start()
|
|||||||
void Core::NetworkAccess::stop()
|
void Core::NetworkAccess::stop()
|
||||||
{
|
{
|
||||||
if (running) {
|
if (running) {
|
||||||
storage.close();
|
files.close();
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
manager = 0;
|
manager = 0;
|
||||||
running = false;
|
running = false;
|
||||||
@ -103,7 +128,9 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||||||
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);
|
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
||||||
|
emit downloadFileProgress(*mItr, progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +146,9 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError 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);
|
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
||||||
|
emit downloadFileError(*mItr, errorText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,43 +276,50 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
|
|
||||||
void Core::NetworkAccess::onDownloadFinished()
|
void Core::NetworkAccess::onDownloadFinished()
|
||||||
{
|
{
|
||||||
|
QString path("");
|
||||||
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);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error downloading" << url << ": the request is done but there is no record of it being downloaded, ignoring";
|
qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
if (dwn->success) {
|
if (dwn->success) {
|
||||||
qDebug() << "download success for" << url;
|
qDebug() << "download success for" << url;
|
||||||
QStringList hops = url.split("/");
|
QStringList hops = url.split("/");
|
||||||
QString fileName = hops.back();
|
QString fileName = hops.back();
|
||||||
QString jid;
|
QStringList parts = fileName.split(".");
|
||||||
if (dwn->messages.size() > 0) {
|
path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/";
|
||||||
jid = dwn->messages.front().jid;
|
QString suffix("");
|
||||||
|
QStringList::const_iterator sItr = parts.begin();
|
||||||
|
QString realName = *sItr;
|
||||||
|
++sItr;
|
||||||
|
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
||||||
|
suffix += "." + (*sItr);
|
||||||
|
}
|
||||||
|
QString postfix("");
|
||||||
|
QFileInfo proposedName(path + realName + postfix + suffix);
|
||||||
|
int counter = 0;
|
||||||
|
while (proposedName.exists()) {
|
||||||
|
postfix = QString("(") + std::to_string(++counter).c_str() + ")";
|
||||||
|
proposedName = QFileInfo(path + realName + postfix + suffix);
|
||||||
}
|
}
|
||||||
QString path = prepareDirectory(jid);
|
|
||||||
if (path.size() > 0) {
|
|
||||||
path = checkFileName(fileName, path);
|
|
||||||
|
|
||||||
|
path = proposedName.absoluteFilePath();
|
||||||
QFile file(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();
|
||||||
storage.setPath(url, path);
|
files.addRecord(url, path);
|
||||||
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;
|
||||||
path = QString();
|
path = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.size() > 0) {
|
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
||||||
emit downloadFileComplete(dwn->messages, path);
|
emit fileLocalPathResponse(*mItr, path);
|
||||||
} else {
|
|
||||||
//TODO do I need to handle the failure here or it's already being handled in error?
|
|
||||||
//emit loadFileError(dwn->messages, path, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dwn->reply->deleteLater();
|
dwn->reply->deleteLater();
|
||||||
@ -292,9 +328,9 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url)
|
void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url)
|
||||||
{
|
{
|
||||||
Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0});
|
Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", url, 0});
|
||||||
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);
|
||||||
@ -305,7 +341,7 @@ void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& ms
|
|||||||
#endif
|
#endif
|
||||||
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
||||||
downloads.insert(std::make_pair(url, dwn));
|
downloads.insert(std::make_pair(url, dwn));
|
||||||
emit loadFileProgress(dwn->messages, 0, false);
|
emit downloadFileProgress(messageId, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
||||||
@ -314,16 +350,16 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
|||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
if (itr == uploads.end()) {
|
if (itr == uploads.end()) {
|
||||||
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 seems like noone 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* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
emit loadFileError(upl->messages, errorText, true);
|
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
||||||
|
emit uploadFileError(*mItr, errorText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO deletion?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,14 +369,17 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error uploading" << url << ": the request is done there is no record of it being uploading, ignoring";
|
qDebug() << "an error uploading" << url << ": the request is done but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
if (upl->success) {
|
if (upl->success) {
|
||||||
qDebug() << "upload success for" << url;
|
qDebug() << "upload success for" << url;
|
||||||
|
files.addRecord(upl->url, upl->path);
|
||||||
|
|
||||||
storage.addFile(upl->messages, upl->url, upl->path);
|
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
||||||
emit uploadFileComplete(upl->messages, upl->url);
|
emit fileLocalPathResponse(*mItr, upl->path);
|
||||||
|
emit uploadFileComplete(*mItr, upl->url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upl->reply->deleteLater();
|
upl->reply->deleteLater();
|
||||||
@ -364,29 +403,94 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
|
|||||||
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);
|
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
||||||
|
emit uploadFileProgress(*mItr, progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path)
|
||||||
|
{
|
||||||
|
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, url, 0});
|
||||||
|
QNetworkRequest req(url);
|
||||||
|
QFile* file = new QFile(path);
|
||||||
|
if (file->open(QIODevice::ReadOnly)) {
|
||||||
|
upl->reply = manager->put(req, file);
|
||||||
|
|
||||||
|
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onUploadError);
|
||||||
|
#else
|
||||||
|
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
||||||
|
uploads.insert(std::make_pair(url, upl));
|
||||||
|
emit downloadFileProgress(messageId, 0);
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't upload file" << path;
|
||||||
|
emit uploadFileError(messageId, "Error opening file");
|
||||||
|
delete file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QString& url, const QString& path)
|
||||||
|
{
|
||||||
|
std::map<QString, Transfer*>::iterator itr = uploads.find(url);
|
||||||
|
if (itr != uploads.end()) {
|
||||||
|
Transfer* upl = itr->second;
|
||||||
|
std::set<QString>::const_iterator mItr = upl->messages.find(messageId);
|
||||||
|
if (mItr == upl->messages.end()) {
|
||||||
|
upl->messages.insert(messageId);
|
||||||
|
}
|
||||||
|
emit uploadFileProgress(messageId, upl->progress);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
QString ePath = files.getRecord(url);
|
||||||
|
if (ePath == path) {
|
||||||
|
QFileInfo info(path);
|
||||||
|
if (info.exists() && info.isFile()) {
|
||||||
|
emit fileLocalPathResponse(messageId, path);
|
||||||
|
} else {
|
||||||
|
files.removeRecord(url);
|
||||||
|
startUpload(messageId, url, path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QFileInfo info(path);
|
||||||
|
if (info.exists() && info.isFile()) {
|
||||||
|
files.changeRecord(url, path);
|
||||||
|
emit fileLocalPathResponse(messageId, path);
|
||||||
|
} else {
|
||||||
|
files.removeRecord(url);
|
||||||
|
startUpload(messageId, url, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const Archive::NotFound& e) {
|
||||||
|
startUpload(messageId, url, path);
|
||||||
|
} catch (const Archive::Unknown& e) {
|
||||||
|
qDebug() << "Error requesting file path on upload:" << e.what();
|
||||||
|
emit uploadFileError(messageId, QString("Database error: ") + e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
||||||
{
|
{
|
||||||
QString p;
|
return ""; //TODO this is a way not to upload some file more then 1 time, here I'm supposed to return that file GET url
|
||||||
|
|
||||||
try {
|
|
||||||
p = storage.getUrl(path);
|
|
||||||
} catch (const Archive::NotFound& err) {
|
|
||||||
|
|
||||||
} catch (...) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
|
bool Core::NetworkAccess::isUploading(const QString& path, const QString& messageId)
|
||||||
|
{
|
||||||
|
return false; //TODO this is a way to avoid parallel uploading of the same files by different chats
|
||||||
|
// message is is supposed to be added to the uploading messageids list
|
||||||
|
// the result should be true if there was an uploading file with this path
|
||||||
|
// message id can be empty, then it's just to check and not to add
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
|
||||||
{
|
{
|
||||||
QFile* file = new QFile(path);
|
QFile* file = new QFile(path);
|
||||||
Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file});
|
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), file});
|
||||||
QNetworkRequest req(put);
|
QNetworkRequest req(put);
|
||||||
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
|
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
|
||||||
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
|
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
|
||||||
@ -402,99 +506,10 @@ void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QStr
|
|||||||
#endif
|
#endif
|
||||||
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
||||||
uploads.insert(std::make_pair(put.toString(), upl));
|
uploads.insert(std::make_pair(put.toString(), upl));
|
||||||
emit loadFileProgress(upl->messages, 0, true);
|
emit downloadFileProgress(messageId, 0);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "couldn't upload file" << path;
|
qDebug() << "couldn't upload file" << path;
|
||||||
emit loadFileError(upl->messages, "Error opening file", true);
|
emit uploadFileError(messageId, "Error opening file");
|
||||||
delete file;
|
delete file;
|
||||||
delete upl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
storage.addFile(url, account, jid, id);
|
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
|
||||||
if (itr != downloads.end()) {
|
|
||||||
itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
storage.addFile(url, path, account, jid, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path)
|
|
||||||
{
|
|
||||||
for (const std::pair<const QString, Transfer*>& pair : uploads) {
|
|
||||||
Transfer* info = pair.second;
|
|
||||||
if (pair.second->path == path) {
|
|
||||||
std::list<Shared::MessageInfo>& messages = info->messages;
|
|
||||||
bool dup = false;
|
|
||||||
for (const Shared::MessageInfo& info : messages) {
|
|
||||||
if (info.account == acc && info.jid == jid && info.messageId == id) {
|
|
||||||
dup = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dup) {
|
|
||||||
info->messages.emplace_back(acc, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
|
||||||
{
|
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
|
||||||
path += "/" + QApplication::applicationName();
|
|
||||||
if (jid.size() > 0) {
|
|
||||||
path += "/" + jid;
|
|
||||||
}
|
|
||||||
QDir location(path);
|
|
||||||
|
|
||||||
if (!location.exists()) {
|
|
||||||
bool res = location.mkpath(path);
|
|
||||||
if (!res) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
|
|
||||||
{
|
|
||||||
QStringList parts = name.split(".");
|
|
||||||
QString suffix("");
|
|
||||||
QStringList::const_iterator sItr = parts.begin();
|
|
||||||
QString realName = *sItr;
|
|
||||||
++sItr;
|
|
||||||
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
|
||||||
suffix += "." + (*sItr);
|
|
||||||
}
|
|
||||||
QString postfix("");
|
|
||||||
QFileInfo proposedName(path + "/" + realName + suffix);
|
|
||||||
int counter = 0;
|
|
||||||
while (proposedName.exists()) {
|
|
||||||
QString count = QString("(") + std::to_string(++counter).c_str() + ")";
|
|
||||||
proposedName = QFileInfo(path + "/" + realName + count + suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
return proposedName.absoluteFilePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
return storage.addMessageAndCheckForPath(url, account, jid, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& path)
|
|
||||||
{
|
|
||||||
return storage.deletedFile(path);
|
|
||||||
}
|
|
||||||
|
@ -29,15 +29,13 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "urlstorage.h"
|
#include "storage.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//TODO Need to describe how to get rid of records when file is no longer reachable;
|
|
||||||
class NetworkAccess : public QObject
|
class NetworkAccess : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -50,27 +48,26 @@ public:
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
QString getFileRemoteUrl(const QString& path);
|
QString getFileRemoteUrl(const QString& path);
|
||||||
QString addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id);
|
bool isUploading(const QString& path, const QString& messageId = "");
|
||||||
void uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
|
void uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
|
||||||
bool checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path);
|
|
||||||
std::list<Shared::MessageInfo> reportPathInvalid(const QString& path);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
||||||
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
void downloadFileProgress(const QString& messageId, qreal value);
|
||||||
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
void downloadFileError(const QString& messageId, const QString& path);
|
||||||
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void uploadFileProgress(const QString& messageId, qreal value);
|
||||||
|
void uploadFileError(const QString& messageId, const QString& path);
|
||||||
|
void uploadFileComplete(const QString& messageId, const QString& url);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void downladFile(const QString& url);
|
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
||||||
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
void downladFileRequest(const QString& messageId, const QString& url);
|
||||||
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
void uploadFileRequest(const QString& messageId, const QString& url, const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
void startDownload(const QString& messageId, const QString& url);
|
||||||
|
void startUpload(const QString& messageId, const QString& url, const QString& path);
|
||||||
QString getErrorText(QNetworkReply::NetworkError code);
|
QString getErrorText(QNetworkReply::NetworkError code);
|
||||||
QString prepareDirectory(const QString& jid);
|
|
||||||
QString checkFileName(const QString& name, const QString& path);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
@ -83,12 +80,12 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
bool running;
|
bool running;
|
||||||
QNetworkAccessManager* manager;
|
QNetworkAccessManager* manager;
|
||||||
UrlStorage storage;
|
Storage files;
|
||||||
std::map<QString, Transfer*> downloads;
|
std::map<QString, Transfer*> downloads;
|
||||||
std::map<QString, Transfer*> uploads;
|
std::map<QString, Transfer*> uploads;
|
||||||
|
|
||||||
struct Transfer {
|
struct Transfer {
|
||||||
std::list<Shared::MessageInfo> messages;
|
std::set<QString> messages;
|
||||||
qreal progress;
|
qreal progress;
|
||||||
QNetworkReply* reply;
|
QNetworkReply* reply;
|
||||||
bool success;
|
bool success;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.3)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(pse)
|
project(pse)
|
||||||
|
|
||||||
if (WITH_KWALLET)
|
if (WITH_KWALLET)
|
||||||
@ -14,7 +14,7 @@ if (WITH_KWALLET)
|
|||||||
kwallet.cpp
|
kwallet.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(kwalletPSE STATIC ${kwalletPSE_SRC})
|
add_library(kwalletPSE ${kwalletPSE_SRC})
|
||||||
|
|
||||||
target_include_directories(kwalletPSE PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
|
target_include_directories(kwalletPSE PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
target_include_directories(kwalletPSE PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
|
target_include_directories(kwalletPSE PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
|
||||||
|
@ -122,22 +122,7 @@ void Core::RosterItem::nextRequest()
|
|||||||
{
|
{
|
||||||
if (syncronizing) {
|
if (syncronizing) {
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
bool last = false;
|
emit historyResponse(responseCache);
|
||||||
if (archiveState == beginning || archiveState == complete) {
|
|
||||||
QString firstId = archive->oldestId();
|
|
||||||
if (responseCache.size() == 0) {
|
|
||||||
if (requestedBefore == firstId) {
|
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (responseCache.front().getId() == firstId) {
|
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (archiveState == empty && responseCache.size() == 0) {
|
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
emit historyResponse(responseCache, last);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (requestCache.size() > 0) {
|
if (requestCache.size() > 0) {
|
||||||
@ -375,11 +360,6 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
archiveState = complete;
|
archiveState = complete;
|
||||||
archive->setFromTheBeginning(true);
|
archive->setFromTheBeginning(true);
|
||||||
}
|
}
|
||||||
if (added == 0 && wasEmpty) {
|
|
||||||
archiveState = empty;
|
|
||||||
nextRequest();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
QString before;
|
QString before;
|
||||||
if (responseCache.size() > 0) {
|
if (responseCache.size() > 0) {
|
||||||
@ -398,7 +378,7 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
} catch (const Archive::Empty& e) {
|
} catch (const Archive::Empty& e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!found || requestedCount > int(responseCache.size())) {
|
if (!found || requestedCount > responseCache.size()) {
|
||||||
if (archiveState == complete) {
|
if (archiveState == complete) {
|
||||||
nextRequest();
|
nextRequest();
|
||||||
} else {
|
} else {
|
||||||
@ -549,7 +529,7 @@ void Core::RosterItem::clearArchiveRequests()
|
|||||||
requestedBefore = "";
|
requestedBefore = "";
|
||||||
for (const std::pair<int, QString>& pair : requestCache) {
|
for (const std::pair<int, QString>& pair : requestCache) {
|
||||||
if (pair.first != -1) {
|
if (pair.first != -1) {
|
||||||
emit historyResponse(responseCache, false); //just to notify those who still waits with whatever happened to be left in caches yet
|
emit historyResponse(responseCache); //just to notify those who still waits with whatever happened to be left in caches yet
|
||||||
}
|
}
|
||||||
responseCache.clear();
|
responseCache.clear();
|
||||||
}
|
}
|
||||||
@ -569,20 +549,3 @@ void Core::RosterItem::downgradeDatabaseState()
|
|||||||
archiveState = ArchiveState::chunk;
|
archiveState = ArchiveState::chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Message Core::RosterItem::getMessage(const QString& id)
|
|
||||||
{
|
|
||||||
for (const Shared::Message& msg : appendCache) {
|
|
||||||
if (msg.getId() == id) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Shared::Message& msg : hisoryCache) {
|
|
||||||
if (msg.getId() == id) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return archive->getElement(id);
|
|
||||||
}
|
|
||||||
|
@ -78,12 +78,10 @@ public:
|
|||||||
void clearArchiveRequests();
|
void clearArchiveRequests();
|
||||||
void downgradeDatabaseState();
|
void downgradeDatabaseState();
|
||||||
|
|
||||||
Shared::Message getMessage(const QString& id);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nameChanged(const QString& name);
|
void nameChanged(const QString& name);
|
||||||
void subscriptionStateChanged(Shared::SubscriptionState state);
|
void subscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
void historyResponse(const std::list<Shared::Message>& messages, bool last);
|
void historyResponse(const std::list<Shared::Message>& messages);
|
||||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
||||||
void avatarChanged(Shared::Avatar, const QString& path);
|
void avatarChanged(Shared::Avatar, const QString& path);
|
||||||
void requestVCard(const QString& jid);
|
void requestVCard(const QString& jid);
|
||||||
|
@ -32,10 +32,11 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
,kwallet()
|
,kwallet()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress);
|
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
|
||||||
connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError);
|
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
|
||||||
connect(&network, &NetworkAccess::downloadFileComplete, this, &Squawk::fileDownloadComplete);
|
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
|
||||||
connect(&network, &NetworkAccess::uploadFileComplete, this, &Squawk::fileUploadComplete);
|
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
|
||||||
|
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
@ -167,7 +168,7 @@ void Core::Squawk::addAccount(
|
|||||||
|
|
||||||
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
||||||
|
|
||||||
connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError);
|
connect(acc, &Account::uploadFileError, this, &Squawk::uploadFileError);
|
||||||
|
|
||||||
QMap<QString, QVariant> map = {
|
QMap<QString, QVariant> map = {
|
||||||
{"login", login},
|
{"login", login},
|
||||||
@ -335,6 +336,17 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
|
|||||||
itr->second->sendMessage(data);
|
itr->second->sendMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path)
|
||||||
|
{
|
||||||
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
|
if (itr == amap.end()) {
|
||||||
|
qDebug("An attempt to send a message with non existing account, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itr->second->sendMessage(data, path);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -345,10 +357,10 @@ void Core::Squawk::requestArchive(const QString& account, const QString& jid, in
|
|||||||
itr->second->requestArchive(jid, count, before);
|
itr->second->requestArchive(jid, count, before);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last)
|
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list)
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit responseArchive(acc->getName(), jid, list, last);
|
emit responseArchive(acc->getName(), jid, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map)
|
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map)
|
||||||
@ -592,9 +604,14 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co
|
|||||||
itr->second->addRoomRequest(jid, nick, password, autoJoin);
|
itr->second->addRoomRequest(jid, nick, password, autoJoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::fileDownloadRequest(const QString& url)
|
void Core::Squawk::fileLocalPathRequest(const QString& messageId, const QString& url)
|
||||||
{
|
{
|
||||||
network.downladFile(url);
|
network.fileLocalPathRequest(messageId, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::downloadFileRequest(const QString& messageId, const QString& url)
|
||||||
|
{
|
||||||
|
network.downladFileRequest(messageId, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
||||||
@ -745,26 +762,3 @@ void Core::Squawk::onWalletResponsePassword(const QString& login, const QString&
|
|||||||
emit changeAccount(login, {{"password", password}});
|
emit changeAccount(login, {{"password", password}});
|
||||||
accountReady();
|
accountReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
|
||||||
emit fileError({{acc->getName(), jid, id}}, errorText, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onLocalPathInvalid(const QString& path)
|
|
||||||
{
|
|
||||||
std::list<Shared::MessageInfo> list = network.reportPathInvalid(path);
|
|
||||||
|
|
||||||
QMap<QString, QVariant> data({
|
|
||||||
{"attachPath", ""}
|
|
||||||
});
|
|
||||||
for (const Shared::MessageInfo& info : list) {
|
|
||||||
AccountsMap::const_iterator itr = amap.find(info.account);
|
|
||||||
if (itr != amap.end()) {
|
|
||||||
itr->second->requestChangeMessage(info.jid, info.messageId, data);
|
|
||||||
} else {
|
|
||||||
qDebug() << "Reacting on failure to reach file" << path << "there was an attempt to change message in account" << info.account << "which doesn't exist, skipping";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -51,39 +51,31 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void quit();
|
void quit();
|
||||||
void ready();
|
void ready();
|
||||||
|
|
||||||
void newAccount(const QMap<QString, QVariant>&);
|
void newAccount(const QMap<QString, QVariant>&);
|
||||||
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
void removeAccount(const QString& account);
|
void removeAccount(const QString& account);
|
||||||
|
|
||||||
void addGroup(const QString& account, const QString& name);
|
void addGroup(const QString& account, const QString& name);
|
||||||
void removeGroup(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 addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
void removeContact(const QString& account, const QString& jid);
|
void removeContact(const QString& account, const QString& jid);
|
||||||
void removeContact(const QString& account, const QString& jid, const QString& group);
|
void removeContact(const QString& account, const QString& jid, const QString& group);
|
||||||
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
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 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 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 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 responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list);
|
||||||
|
|
||||||
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
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 changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void removeRoom(const QString& account, const QString jid);
|
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 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);
|
||||||
|
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
||||||
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
void downloadFileError(const QString& messageId, const QString& error);
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
void downloadFileProgress(const QString& messageId, qreal value);
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void uploadFileError(const QString& messageId, const QString& error);
|
||||||
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void uploadFileProgress(const QString& messageId, qreal value);
|
||||||
|
|
||||||
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);
|
void requestPassword(const QString& account);
|
||||||
@ -91,18 +83,15 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void newAccountRequest(const QMap<QString, QVariant>& map);
|
void newAccountRequest(const QMap<QString, QVariant>& map);
|
||||||
void modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map);
|
void modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map);
|
||||||
void removeAccountRequest(const QString& name);
|
void removeAccountRequest(const QString& name);
|
||||||
void connectAccount(const QString& account);
|
void connectAccount(const QString& account);
|
||||||
void disconnectAccount(const QString& account);
|
void disconnectAccount(const QString& account);
|
||||||
|
|
||||||
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 sendMessage(const QString& account, const Shared::Message& data, const QString& path);
|
||||||
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);
|
||||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
@ -110,18 +99,15 @@ public slots:
|
|||||||
void removeContactRequest(const QString& account, const QString& jid);
|
void removeContactRequest(const QString& account, const QString& jid);
|
||||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||||
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 setRoomJoined(const QString& account, const QString& jid, bool joined);
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
void setRoomAutoJoin(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 fileLocalPathRequest(const QString& messageId, const QString& url);
|
||||||
void fileDownloadRequest(const QString& url);
|
void downloadFileRequest(const QString& messageId, 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 responsePassword(const QString& account, const QString& password);
|
void responsePassword(const QString& account, const QString& password);
|
||||||
void onLocalPathInvalid(const QString& path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::deque<Account*> Accounts;
|
typedef std::deque<Account*> Accounts;
|
||||||
@ -160,7 +146,7 @@ private slots:
|
|||||||
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemovePresence(const QString& jid, const QString& name);
|
void onAccountRemovePresence(const QString& jid, const QString& name);
|
||||||
void onAccountMessage(const Shared::Message& data);
|
void onAccountMessage(const Shared::Message& data);
|
||||||
void onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last);
|
void onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list);
|
||||||
void onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data);
|
void onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data);
|
void onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemoveRoom(const QString jid);
|
void onAccountRemoveRoom(const QString jid);
|
||||||
@ -169,8 +155,6 @@ private slots:
|
|||||||
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 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 onWalletResponsePassword(const QString& login, const QString& password);
|
||||||
void onWalletRejectPassword(const QString& login);
|
void onWalletRejectPassword(const QString& login);
|
||||||
|
@ -1,491 +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 <QStandardPaths>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "urlstorage.h"
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlStorage(const QString& p_name):
|
|
||||||
name(p_name),
|
|
||||||
opened(false),
|
|
||||||
environment(),
|
|
||||||
base(),
|
|
||||||
map()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::UrlStorage::~UrlStorage()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::open()
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
mdb_env_create(&environment);
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
|
||||||
path += "/" + name;
|
|
||||||
QDir cache(path);
|
|
||||||
|
|
||||||
if (!cache.exists()) {
|
|
||||||
bool res = cache.mkpath(path);
|
|
||||||
if (!res) {
|
|
||||||
throw Archive::Directory(path.toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, 2);
|
|
||||||
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
|
|
||||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
|
|
||||||
mdb_dbi_open(txn, "map", MDB_CREATE, &map);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
opened = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::close()
|
|
||||||
{
|
|
||||||
if (opened) {
|
|
||||||
mdb_dbi_close(environment, map);
|
|
||||||
mdb_dbi_close(environment, base);
|
|
||||||
mdb_env_close(environment);
|
|
||||||
opened = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite)
|
|
||||||
{
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
|
|
||||||
try {
|
|
||||||
writeInfo(key, info, txn, overwrite);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite)
|
|
||||||
{
|
|
||||||
QByteArray ba;
|
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
|
||||||
info.serialize(ds);
|
|
||||||
|
|
||||||
const std::string& id = key.toStdString();
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
lmdbData.mv_size = ba.size();
|
|
||||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, overwrite ? 0 : MDB_NOOVERWRITE);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
if (rc == MDB_KEYEXIST) {
|
|
||||||
if (!overwrite) {
|
|
||||||
throw Archive::Exist(name.toStdString(), id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.hasPath()) {
|
|
||||||
std::string sp = info.getPath().toStdString();
|
|
||||||
lmdbData.mv_size = sp.size();
|
|
||||||
lmdbData.mv_data = (char*)sp.c_str();
|
|
||||||
rc = mdb_put(txn, map, &lmdbData, &lmdbKey, 0);
|
|
||||||
if (rc != 0) {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info, MDB_txn* txn)
|
|
||||||
{
|
|
||||||
const std::string& id = key.toStdString();
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
int rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
QDataStream ds(&ba, QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
info.deserialize(ds);
|
|
||||||
} else if (rc == MDB_NOTFOUND) {
|
|
||||||
throw Archive::NotFound(id, name.toStdString());
|
|
||||||
} else {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info)
|
|
||||||
{
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
|
|
||||||
try {
|
|
||||||
readInfo(key, info, txn);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::addFile(const QString& url)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addFile(no message, no path)", name.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
addToInfo(url, "", "", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::addFile(const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addFile(no message, with path)", name.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
addToInfo(url, "", "", "", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addFile(with message, no path)", name.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
addToInfo(url, account, jid, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addFile(with message, with path)", name.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
addToInfo(url, account, jid, id, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addFile(with list)", name.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
UrlInfo info (path, msgs);
|
|
||||||
writeInfo(url, info, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addMessageAndCheckForPath", name.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return addToInfo(url, account, jid, id).getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path)
|
|
||||||
{
|
|
||||||
UrlInfo info;
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
|
|
||||||
try {
|
|
||||||
readInfo(url, info, txn);
|
|
||||||
} catch (const Archive::NotFound& e) {
|
|
||||||
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pathChange = false;
|
|
||||||
bool listChange = false;
|
|
||||||
if (path != "-s") {
|
|
||||||
if (info.getPath() != path) {
|
|
||||||
info.setPath(path);
|
|
||||||
pathChange = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.size() > 0 && jid.size() > 0 && id.size() > 0) {
|
|
||||||
listChange = info.addMessage(account, jid, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathChange || listChange) {
|
|
||||||
try {
|
|
||||||
writeInfo(url, info, txn, true);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
std::list<Shared::MessageInfo> list;
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
UrlInfo info;
|
|
||||||
|
|
||||||
try {
|
|
||||||
readInfo(url, info, txn);
|
|
||||||
info.getMessages(list);
|
|
||||||
} catch (const Archive::NotFound& e) {
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.setPath(path);
|
|
||||||
try {
|
|
||||||
writeInfo(url, info, txn, true);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url)
|
|
||||||
{
|
|
||||||
std::list<Shared::MessageInfo> list;
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
UrlInfo info;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string id = url.toStdString();
|
|
||||||
readInfo(url, info, txn);
|
|
||||||
info.getMessages(list);
|
|
||||||
|
|
||||||
MDB_val lmdbKey;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
int rc = mdb_del(txn, base, &lmdbKey, NULL);
|
|
||||||
if (rc != 0) {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.hasPath()) {
|
|
||||||
std::string path = info.getPath().toStdString();
|
|
||||||
lmdbKey.mv_size = path.size();
|
|
||||||
lmdbKey.mv_data = (char*)path.c_str();
|
|
||||||
|
|
||||||
int rc = mdb_del(txn, map, &lmdbKey, NULL);
|
|
||||||
if (rc != 0) {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path)
|
|
||||||
{
|
|
||||||
std::list<Shared::MessageInfo> list;
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::string spath = path.toStdString();
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = spath.size();
|
|
||||||
lmdbKey.mv_data = (char*)spath.c_str();
|
|
||||||
|
|
||||||
QString url;
|
|
||||||
int rc = mdb_get(txn, map, &lmdbKey, &lmdbData);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
url = QString(surl.c_str());
|
|
||||||
} else if (rc == MDB_NOTFOUND) {
|
|
||||||
qDebug() << "Have been asked to remove file" << path << ", which isn't in the database, skipping";
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return list;
|
|
||||||
} else {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
|
|
||||||
UrlInfo info;
|
|
||||||
std::string id = url.toStdString();
|
|
||||||
readInfo(url, info, txn);
|
|
||||||
info.getMessages(list);
|
|
||||||
info.setPath(QString());
|
|
||||||
writeInfo(url, info, txn, true);
|
|
||||||
|
|
||||||
rc = mdb_del(txn, map, &lmdbKey, NULL);
|
|
||||||
if (rc != 0) {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString Core::UrlStorage::getUrl(const QString& path)
|
|
||||||
{
|
|
||||||
std::list<Shared::MessageInfo> list;
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
|
|
||||||
std::string spath = path.toStdString();
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = spath.size();
|
|
||||||
lmdbKey.mv_data = (char*)spath.c_str();
|
|
||||||
|
|
||||||
QString url;
|
|
||||||
int rc = mdb_get(txn, map, &lmdbKey, &lmdbData);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
url = QString(surl.c_str());
|
|
||||||
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return url;
|
|
||||||
} else if (rc == MDB_NOTFOUND) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw Archive::NotFound(spath, name.toStdString());
|
|
||||||
} else {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<QString, std::list<Shared::MessageInfo>> Core::UrlStorage::getPath(const QString& url)
|
|
||||||
{
|
|
||||||
UrlInfo info;
|
|
||||||
readInfo(url, info);
|
|
||||||
std::list<Shared::MessageInfo> container;
|
|
||||||
info.getMessages(container);
|
|
||||||
return std::make_pair(info.getPath(), container);
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlInfo::UrlInfo():
|
|
||||||
localPath(),
|
|
||||||
messages() {}
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path):
|
|
||||||
localPath(path),
|
|
||||||
messages() {}
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs):
|
|
||||||
localPath(path),
|
|
||||||
messages(msgs) {}
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlInfo::~UrlInfo() {}
|
|
||||||
|
|
||||||
bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id)
|
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : messages) {
|
|
||||||
if (info.account == acc && info.jid == jid && info.messageId == id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages.emplace_back(acc, jid, id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const
|
|
||||||
{
|
|
||||||
data << localPath;
|
|
||||||
std::list<Shared::MessageInfo>::size_type size = messages.size();
|
|
||||||
data << quint32(size);
|
|
||||||
for (const Shared::MessageInfo& info : messages) {
|
|
||||||
data << info.account;
|
|
||||||
data << info.jid;
|
|
||||||
data << info.messageId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data)
|
|
||||||
{
|
|
||||||
data >> localPath;
|
|
||||||
quint32 size;
|
|
||||||
data >> size;
|
|
||||||
for (quint32 i = 0; i < size; ++i) {
|
|
||||||
messages.emplace_back();
|
|
||||||
Shared::MessageInfo& info = messages.back();
|
|
||||||
data >> info.account;
|
|
||||||
data >> info.jid;
|
|
||||||
data >> info.messageId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::UrlStorage::UrlInfo::getMessages(std::list<Shared::MessageInfo>& container) const
|
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : messages) {
|
|
||||||
container.emplace_back(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::UrlStorage::UrlInfo::getPath() const
|
|
||||||
{
|
|
||||||
return localPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::UrlStorage::UrlInfo::hasPath() const
|
|
||||||
{
|
|
||||||
return localPath.size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Core::UrlStorage::UrlInfo::setPath(const QString& path)
|
|
||||||
{
|
|
||||||
localPath = path;
|
|
||||||
}
|
|
@ -1,99 +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_URLSTORAGE_H
|
|
||||||
#define CORE_URLSTORAGE_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QDataStream>
|
|
||||||
#include <lmdb.h>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "archive.h"
|
|
||||||
#include <shared/messageinfo.h>
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
class UrlStorage
|
|
||||||
{
|
|
||||||
class UrlInfo;
|
|
||||||
public:
|
|
||||||
UrlStorage(const QString& name);
|
|
||||||
~UrlStorage();
|
|
||||||
|
|
||||||
void open();
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void addFile(const QString& url);
|
|
||||||
void addFile(const QString& url, const QString& path);
|
|
||||||
void addFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
|
||||||
void addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
|
||||||
void addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path); //this one overwrites all that was
|
|
||||||
std::list<Shared::MessageInfo> removeFile(const QString& url); //removes entry like it never was in the database, returns affected message infos
|
|
||||||
std::list<Shared::MessageInfo> deletedFile(const QString& path); //empties the localPath of the entry, returns affected message infos
|
|
||||||
std::list<Shared::MessageInfo> setPath(const QString& url, const QString& path);
|
|
||||||
QString getUrl(const QString& path);
|
|
||||||
QString addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id);
|
|
||||||
std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString name;
|
|
||||||
bool opened;
|
|
||||||
MDB_env* environment;
|
|
||||||
MDB_dbi base;
|
|
||||||
MDB_dbi map;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false);
|
|
||||||
void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false);
|
|
||||||
void readInfo(const QString& key, UrlInfo& info);
|
|
||||||
void readInfo(const QString& key, UrlInfo& info, MDB_txn* txn);
|
|
||||||
UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s");
|
|
||||||
|
|
||||||
private:
|
|
||||||
class UrlInfo {
|
|
||||||
public:
|
|
||||||
UrlInfo(const QString& path);
|
|
||||||
UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs);
|
|
||||||
UrlInfo();
|
|
||||||
~UrlInfo();
|
|
||||||
|
|
||||||
void serialize(QDataStream& data) const;
|
|
||||||
void deserialize(QDataStream& data);
|
|
||||||
|
|
||||||
QString getPath() const;
|
|
||||||
bool hasPath() const;
|
|
||||||
void setPath(const QString& path);
|
|
||||||
|
|
||||||
bool addMessage(const QString& acc, const QString& jid, const QString& id);
|
|
||||||
void getMessages(std::list<Shared::MessageInfo>& container) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString localPath;
|
|
||||||
std::list<Shared::MessageInfo> messages;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CORE_URLSTORAGE_H
|
|
2
external/simpleCrypt/CMakeLists.txt
vendored
@ -10,7 +10,7 @@ set(simplecrypt_SRC
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
# Tell CMake to create the helloworld executable
|
||||||
add_library(simpleCrypt STATIC ${simplecrypt_SRC})
|
add_library(simpleCrypt ${simplecrypt_SRC})
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
# Use the Widgets module from Qt 5.
|
||||||
target_link_libraries(simpleCrypt Qt5::Core)
|
target_link_libraries(simpleCrypt Qt5::Core)
|
||||||
|
21
main.cpp
@ -20,7 +20,6 @@
|
|||||||
#include "core/squawk.h"
|
#include "core/squawk.h"
|
||||||
#include "signalcatcher.h"
|
#include "signalcatcher.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
#include "shared/messageinfo.h"
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
@ -32,10 +31,8 @@
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||||
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
|
||||||
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
||||||
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
||||||
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
|
||||||
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
||||||
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
||||||
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
||||||
@ -99,7 +96,10 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
||||||
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
||||||
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
||||||
QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage);
|
QObject::connect(&w, qOverload<const QString&, const Shared::Message&>(&Squawk::sendMessage),
|
||||||
|
squawk, qOverload<const QString&, const Shared::Message&>(&Core::Squawk::sendMessage));
|
||||||
|
QObject::connect(&w, qOverload<const QString&, const Shared::Message&, const QString&>(&Squawk::sendMessage),
|
||||||
|
squawk, qOverload<const QString&, const Shared::Message&, const QString&>(&Core::Squawk::sendMessage));
|
||||||
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
||||||
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
||||||
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
||||||
@ -109,14 +109,14 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
||||||
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
||||||
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
||||||
QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest);
|
QObject::connect(&w, &Squawk::fileLocalPathRequest, squawk, &Core::Squawk::fileLocalPathRequest);
|
||||||
|
QObject::connect(&w, &Squawk::downloadFileRequest, squawk, &Core::Squawk::downloadFileRequest);
|
||||||
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
||||||
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
||||||
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
||||||
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
||||||
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
||||||
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
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::newAccount, &w, &Squawk::newAccount);
|
||||||
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
||||||
@ -141,10 +141,11 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
||||||
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
||||||
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
||||||
QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete);
|
QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse);
|
||||||
QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete);
|
QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::fileProgress);
|
||||||
QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress);
|
QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::fileError);
|
||||||
QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError);
|
QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError);
|
||||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
||||||
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
||||||
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.3)
|
|
||||||
project(plugins)
|
|
||||||
|
|
||||||
if (WITH_KIO)
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
|
|
||||||
find_package(Qt5Core CONFIG REQUIRED)
|
|
||||||
|
|
||||||
set(openFileManagerWindowJob_SRC
|
|
||||||
openfilemanagerwindowjob.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
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()
|
|
@ -1,8 +0,0 @@
|
|||||||
#include <QUrl>
|
|
||||||
#include <QObject>
|
|
||||||
#include <KIO/OpenFileManagerWindowJob>
|
|
||||||
|
|
||||||
extern "C" void highlightInFileManager(const QUrl& url) {
|
|
||||||
KIO::OpenFileManagerWindowJob* job = KIO::highlightInFileManager({url});
|
|
||||||
QObject::connect(job, &KIO::OpenFileManagerWindowJob::result, job, &KIO::OpenFileManagerWindowJob::deleteLater);
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE svg>
|
|
||||||
<svg viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<style type="text/css" id="current-color-scheme">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#232629;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 4 3 L 4 19 L 15 19 L 15 18 L 5 18 L 5 4 L 13 4 L 13 8 L 17 8 L 17 16 L 18 16 L 18 7 L 14 3 L 4 3 Z M 13 11 C 11.338 11 10 12.338 10 14 C 10 15.662 11.338 17 13 17 C 13.6494 17 14.2464 16.7914 14.7363 16.4434 L 17.293 19 L 18 18.293 L 15.4434 15.7363 C 15.7914 15.2464 16 14.6494 16 14 C 16 12.338 14.662 11 13 11 Z M 13 12 C 14.108 12 15 12.892 15 14 C 15 15.108 14.108 16 13 16 C 11.892 16 11 15.108 11 14 C 11 12.892 11.892 12 13 12 Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 807 B |
@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="32" version="1.1" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 32 32" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
|
||||||
<defs id="defs5455">
|
|
||||||
<linearGradient inkscape:collect="always" id="linearGradient4172-5">
|
|
||||||
<stop style="stop-color:#3daee9" id="stop4174-6"/>
|
|
||||||
<stop offset="1" style="stop-color:#6cc1ef" id="stop4176-6"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4172-5" id="linearGradient4342" y1="29" y2="8" x2="0" gradientUnits="userSpaceOnUse"/>
|
|
||||||
</defs>
|
|
||||||
<metadata id="metadata5458"/>
|
|
||||||
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="matrix(1 0 0 1 -384.57143 -515.798)">
|
|
||||||
<path inkscape:connector-curvature="0" style="fill:#147eb8;fill-rule:evenodd" id="path4308" d="m 386.57144,518.798 0,7 0,1 28,0 0,-6 -14.00001,0 -2,-2 z"/>
|
|
||||||
<path inkscape:connector-curvature="0" style="fill-opacity:0.235294;fill-rule:evenodd" id="path4306" d="m 397.57143,523.798 -1.99999,1 -9,0 0,1 6.99999,0 3,0 z"/>
|
|
||||||
<path style="fill:url(#linearGradient4342)" id="rect4294" d="M 13 8 L 11 10 L 2 10 L 1 10 L 1 29 L 12 29 L 13 29 L 31 29 L 31 8 L 13 8 z " transform="matrix(1 0 0 1 384.57143 515.798)"/>
|
|
||||||
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4304" d="m 397.57143,523.798 -2,2 -10,0 0,1 11,0 z"/>
|
|
||||||
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4310" d="m 398.57143,518.798 1,3 15.00001,0 0,-1 -14.00001,0 z"/>
|
|
||||||
<rect width="30" x="385.57144" y="543.79797" height="1" style="fill-opacity:0.235294" id="rect4292"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE svg>
|
|
||||||
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<style type="text/css" id="current-color-scheme">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#232629;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 3 2 L 3 14 L 10 14 L 10 13 L 4 13 L 4 3 L 9 3 L 9 6 L 12 6 L 12 11 L 13 11 L 13 5 L 10 2 L 3 2 Z"/>
|
|
||||||
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 8.48828 7 C 7.10757 7 5.98828 8.11929 5.98828 9.5 C 5.98828 10.8807 7.10757 12 8.48828 12 C 8.97811 11.9992 9.45691 11.8546 9.86523 11.584 L 12.2813 14 L 12.9883 13.293 L 10.5723 10.877 C 10.8428 10.4686 10.9875 9.98983 10.9883 9.5 C 10.9883 8.11929 9.86899 7 8.48828 7 Z M 8.48828 8 C 9.31671 8 9.98828 8.67157 9.98828 9.5 C 9.98828 10.3284 9.31671 11 8.48828 11 C 7.65985 11 6.98828 10.3284 6.98828 9.5 C 6.98828 8.67157 7.65985 8 8.48828 8 Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1010 B |
@ -1,13 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
||||||
<defs id="defs3051">
|
|
||||||
<style type="text/css" id="current-color-scheme">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#232629;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
|
||||||
d="M 2 2 L 2 3 L 2 6 L 2 7 L 2 13 L 2 14 L 14 14 L 14 13 L 14 6 L 14 5 L 14 4 L 9.0078125 4 L 7.0078125 2 L 7 2.0078125 L 7 2 L 3 2 L 2 2 z M 3 3 L 6.5917969 3 L 7.59375 4 L 7 4 L 7 4.0078125 L 6.9921875 4 L 4.9921875 6 L 3 6 L 3 3 z M 3 7 L 13 7 L 13 13 L 3 13 L 3 7 z "
|
|
||||||
class="ColorScheme-Text"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 609 B |
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE svg>
|
|
||||||
<svg viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<style type="text/css" id="current-color-scheme">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#eff0f1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 4 3 L 4 19 L 15 19 L 15 18 L 5 18 L 5 4 L 13 4 L 13 8 L 17 8 L 17 16 L 18 16 L 18 7 L 14 3 L 4 3 Z M 13 11 C 11.338 11 10 12.338 10 14 C 10 15.662 11.338 17 13 17 C 13.6494 17 14.2464 16.7914 14.7363 16.4434 L 17.293 19 L 18 18.293 L 15.4434 15.7363 C 15.7914 15.2464 16 14.6494 16 14 C 16 12.338 14.662 11 13 11 Z M 13 12 C 14.108 12 15 12.892 15 14 C 15 15.108 14.108 16 13 16 C 11.892 16 11 15.108 11 14 C 11 12.892 11.892 12 13 12 Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 807 B |
@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
<svg width="32" version="1.1" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 32 32" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
|
||||||
<defs id="defs5455">
|
|
||||||
<linearGradient inkscape:collect="always" id="linearGradient4172-5">
|
|
||||||
<stop style="stop-color:#3daee9" id="stop4174-6"/>
|
|
||||||
<stop offset="1" style="stop-color:#6cc1ef" id="stop4176-6"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4172-5" id="linearGradient4342" y1="29" y2="8" x2="0" gradientUnits="userSpaceOnUse"/>
|
|
||||||
</defs>
|
|
||||||
<metadata id="metadata5458"/>
|
|
||||||
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="matrix(1 0 0 1 -384.57143 -515.798)">
|
|
||||||
<path inkscape:connector-curvature="0" style="fill:#147eb8;fill-rule:evenodd" id="path4308" d="m 386.57144,518.798 0,7 0,1 28,0 0,-6 -14.00001,0 -2,-2 z"/>
|
|
||||||
<path inkscape:connector-curvature="0" style="fill-opacity:0.235294;fill-rule:evenodd" id="path4306" d="m 397.57143,523.798 -1.99999,1 -9,0 0,1 6.99999,0 3,0 z"/>
|
|
||||||
<path style="fill:url(#linearGradient4342)" id="rect4294" d="M 13 8 L 11 10 L 2 10 L 1 10 L 1 29 L 12 29 L 13 29 L 31 29 L 31 8 L 13 8 z " transform="matrix(1 0 0 1 384.57143 515.798)"/>
|
|
||||||
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4304" d="m 397.57143,523.798 -2,2 -10,0 0,1 11,0 z"/>
|
|
||||||
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4310" d="m 398.57143,518.798 1,3 15.00001,0 0,-1 -14.00001,0 z"/>
|
|
||||||
<rect width="30" x="385.57144" y="543.79797" height="1" style="fill-opacity:0.235294" id="rect4292"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.8 KiB |
@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE svg>
|
|
||||||
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<style type="text/css" id="current-color-scheme">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#eff0f1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 3 2 L 3 14 L 10 14 L 10 13 L 4 13 L 4 3 L 9 3 L 9 6 L 12 6 L 12 11 L 13 11 L 13 5 L 10 2 L 3 2 Z"/>
|
|
||||||
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 8.48828 7 C 7.10757 7 5.98828 8.11929 5.98828 9.5 C 5.98828 10.8807 7.10757 12 8.48828 12 C 8.97811 11.9992 9.45691 11.8546 9.86523 11.584 L 12.2813 14 L 12.9883 13.293 L 10.5723 10.877 C 10.8428 10.4686 10.9875 9.98983 10.9883 9.5 C 10.9883 8.11929 9.86899 7 8.48828 7 Z M 8.48828 8 C 9.31671 8 9.98828 8.67157 9.98828 9.5 C 9.98828 10.3284 9.31671 11 8.48828 11 C 7.65985 11 6.98828 10.3284 6.98828 9.5 C 6.98828 8.67157 7.65985 8 8.48828 8 Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1010 B |
@ -1,13 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
||||||
<defs id="defs3051">
|
|
||||||
<style type="text/css" id="current-color-scheme">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#eff0f1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
|
||||||
d="M 2 2 L 2 3 L 2 6 L 2 7 L 2 13 L 2 14 L 14 14 L 14 13 L 14 6 L 14 5 L 14 4 L 9.0078125 4 L 7.0078125 2 L 7 2.0078125 L 7 2 L 3 2 L 2 2 z M 3 3 L 6.5917969 3 L 7.59375 4 L 7 4 L 7 4.0078125 L 6.9921875 4 L 4.9921875 6 L 3 6 L 3 3 z M 3 7 L 13 7 L 13 13 L 3 13 L 3 7 z "
|
|
||||||
class="ColorScheme-Text"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 609 B |
@ -40,8 +40,6 @@
|
|||||||
<file>images/fallback/dark/big/favorite.svg</file>
|
<file>images/fallback/dark/big/favorite.svg</file>
|
||||||
<file>images/fallback/dark/big/unfavorite.svg</file>
|
<file>images/fallback/dark/big/unfavorite.svg</file>
|
||||||
<file>images/fallback/dark/big/add.svg</file>
|
<file>images/fallback/dark/big/add.svg</file>
|
||||||
<file>images/fallback/dark/big/folder.svg</file>
|
|
||||||
<file>images/fallback/dark/big/document-preview.svg</file>
|
|
||||||
|
|
||||||
|
|
||||||
<file>images/fallback/dark/small/absent.svg</file>
|
<file>images/fallback/dark/small/absent.svg</file>
|
||||||
@ -82,8 +80,6 @@
|
|||||||
<file>images/fallback/dark/small/favorite.svg</file>
|
<file>images/fallback/dark/small/favorite.svg</file>
|
||||||
<file>images/fallback/dark/small/unfavorite.svg</file>
|
<file>images/fallback/dark/small/unfavorite.svg</file>
|
||||||
<file>images/fallback/dark/small/add.svg</file>
|
<file>images/fallback/dark/small/add.svg</file>
|
||||||
<file>images/fallback/dark/small/folder.svg</file>
|
|
||||||
<file>images/fallback/dark/small/document-preview.svg</file>
|
|
||||||
|
|
||||||
|
|
||||||
<file>images/fallback/light/big/absent.svg</file>
|
<file>images/fallback/light/big/absent.svg</file>
|
||||||
@ -124,8 +120,6 @@
|
|||||||
<file>images/fallback/light/big/favorite.svg</file>
|
<file>images/fallback/light/big/favorite.svg</file>
|
||||||
<file>images/fallback/light/big/unfavorite.svg</file>
|
<file>images/fallback/light/big/unfavorite.svg</file>
|
||||||
<file>images/fallback/light/big/add.svg</file>
|
<file>images/fallback/light/big/add.svg</file>
|
||||||
<file>images/fallback/light/big/folder.svg</file>
|
|
||||||
<file>images/fallback/light/big/document-preview.svg</file>
|
|
||||||
|
|
||||||
|
|
||||||
<file>images/fallback/light/small/absent.svg</file>
|
<file>images/fallback/light/small/absent.svg</file>
|
||||||
@ -166,7 +160,5 @@
|
|||||||
<file>images/fallback/light/small/favorite.svg</file>
|
<file>images/fallback/light/small/favorite.svg</file>
|
||||||
<file>images/fallback/light/small/unfavorite.svg</file>
|
<file>images/fallback/light/small/unfavorite.svg</file>
|
||||||
<file>images/fallback/light/small/add.svg</file>
|
<file>images/fallback/light/small/add.svg</file>
|
||||||
<file>images/fallback/light/small/folder.svg</file>
|
|
||||||
<file>images/fallback/light/small/document-preview.svg</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
1
shared.h
@ -25,6 +25,5 @@
|
|||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/vcard.h"
|
#include "shared/vcard.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
#include "shared/messageinfo.h"
|
|
||||||
|
|
||||||
#endif // SHARED_H
|
#endif // SHARED_H
|
||||||
|
@ -23,11 +23,6 @@
|
|||||||
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"};
|
||||||
|
|
||||||
#ifdef WITH_KIO
|
|
||||||
QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob");
|
|
||||||
Shared::Global::HighlightInFileManager Shared::Global::hfm = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Shared::Global::Global():
|
Shared::Global::Global():
|
||||||
availability({
|
availability({
|
||||||
tr("Online", "Availability"),
|
tr("Online", "Availability"),
|
||||||
@ -85,61 +80,16 @@ Shared::Global::Global():
|
|||||||
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")
|
||||||
}),
|
}),
|
||||||
pluginSupport({
|
pluginSupport({
|
||||||
{"KWallet", false},
|
{"KWallet", false}
|
||||||
{"openFileManagerWindowJob", false}
|
})
|
||||||
}),
|
|
||||||
fileCache()
|
|
||||||
{
|
{
|
||||||
if (instance != 0) {
|
if (instance != 0) {
|
||||||
throw 551;
|
throw 551;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
#ifdef WITH_KIO
|
|
||||||
openFileManagerWindowJob.load();
|
|
||||||
if (openFileManagerWindowJob.isLoaded()) {
|
|
||||||
hfm = (HighlightInFileManager) openFileManagerWindowJob.resolve("highlightInFileManager");
|
|
||||||
if (hfm) {
|
|
||||||
setSupported("openFileManagerWindowJob", true);
|
|
||||||
qDebug() << "KIO::OpenFileManagerWindow support enabled";
|
|
||||||
} else {
|
|
||||||
qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't resolve required methods in the library";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't load the library" << openFileManagerWindowJob.errorString();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
|
|
||||||
{
|
|
||||||
std::map<QString, FileInfo>::const_iterator itr = instance->fileCache.find(path);
|
|
||||||
if (itr == instance->fileCache.end()) {
|
|
||||||
QMimeDatabase db;
|
|
||||||
QMimeType type = db.mimeTypeForFile(path);
|
|
||||||
QStringList parts = type.name().split("/");
|
|
||||||
QString big = parts.front();
|
|
||||||
QFileInfo info(path);
|
|
||||||
|
|
||||||
FileInfo::Preview p = FileInfo::Preview::none;
|
|
||||||
QSize size;
|
|
||||||
if (big == "image") {
|
|
||||||
if (parts.back() == "gif") {
|
|
||||||
//TODO need to consider GIF as a movie
|
|
||||||
}
|
|
||||||
p = FileInfo::Preview::picture;
|
|
||||||
QImage img(path);
|
|
||||||
size = img.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.fileName(), size, type, p}))).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
return itr->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Shared::Global * Shared::Global::getInstance()
|
Shared::Global * Shared::Global::getInstance()
|
||||||
{
|
{
|
||||||
return instance;
|
return instance;
|
||||||
@ -202,69 +152,6 @@ QString Shared::Global::getDescription(Shared::AccountPassword ap)
|
|||||||
return instance->accountPasswordDescription[static_cast<int>(ap)];
|
return instance->accountPasswordDescription[static_cast<int>(ap)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const QStringList query = {"query", "default", "inode/directory"};
|
|
||||||
static const QRegularExpression dolphinReg("[Dd]olphin");
|
|
||||||
static const QRegularExpression nautilusReg("[Nn]autilus");
|
|
||||||
static const QRegularExpression cajaReg("[Cc]aja");
|
|
||||||
static const QRegularExpression nemoReg("[Nn]emo");
|
|
||||||
static const QRegularExpression konquerorReg("kfmclient");
|
|
||||||
static const QRegularExpression pcmanfmQtReg("pcmanfm-qt");
|
|
||||||
static const QRegularExpression pcmanfmReg("pcmanfm");
|
|
||||||
static const QRegularExpression thunarReg("thunar");
|
|
||||||
|
|
||||||
void Shared::Global::highlightInFileManager(const QString& path)
|
|
||||||
{
|
|
||||||
#ifdef WITH_KIO
|
|
||||||
if (supported("openFileManagerWindowJob")) {
|
|
||||||
hfm(path);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: KIO plugin isn't loaded, trying fallback";
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: squawk wasn't compiled to support it, trying fallback";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QFileInfo info = path;
|
|
||||||
if (info.exists()) {
|
|
||||||
QProcess proc;
|
|
||||||
proc.start("xdg-mime", query);
|
|
||||||
proc.waitForFinished();
|
|
||||||
QString output = proc.readLine().simplified();
|
|
||||||
|
|
||||||
QString folder;
|
|
||||||
if (info.isDir()) {
|
|
||||||
folder = info.canonicalFilePath();
|
|
||||||
} else {
|
|
||||||
folder = info.canonicalPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output.contains(dolphinReg)) {
|
|
||||||
//there is a bug on current (21.04.0) dolphin, it works correct only if you already have dolphin launched
|
|
||||||
proc.startDetached("dolphin", QStringList() << "--select" << info.canonicalFilePath());
|
|
||||||
//KIO::highlightInFileManager({QUrl(info.canonicalFilePath())});
|
|
||||||
} else if (output.contains(nautilusReg)) {
|
|
||||||
proc.startDetached("nautilus", QStringList() << "--select" << info.canonicalFilePath()); //this worked on nautilus
|
|
||||||
} else if (output.contains(cajaReg)) {
|
|
||||||
proc.startDetached("caja", QStringList() << folder); //caja doesn't seem to support file selection command line, gonna just open directory
|
|
||||||
} else if (output.contains(nemoReg)) {
|
|
||||||
proc.startDetached("nemo", QStringList() << info.canonicalFilePath()); //nemo supports selecting files without keys
|
|
||||||
} else if (output.contains(konquerorReg)) {
|
|
||||||
proc.startDetached("konqueror", QStringList() << "--select" << info.canonicalFilePath()); //this worked on konqueror
|
|
||||||
} else if (output.contains(pcmanfmQtReg)) {
|
|
||||||
proc.startDetached("pcmanfm-qt", QStringList() << folder); //pcmanfm-qt doesn't seem to support open with selection, gonna just open directory
|
|
||||||
} else if (output.contains(pcmanfmReg)) {
|
|
||||||
proc.startDetached("pcmanfm", QStringList() << folder); //pcmanfm also doesn't seem to support open with selection, gonna just open directory
|
|
||||||
} else if (output.contains(thunarReg)) {
|
|
||||||
proc.startDetached("thunar", QStringList() << folder); //thunar doesn't seem to support open with selection, gonna just open directory
|
|
||||||
} else {
|
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define FROM_INT_INPL(Enum) \
|
#define FROM_INT_INPL(Enum) \
|
||||||
template<> \
|
template<> \
|
||||||
Enum Shared::Global::fromInt(int src) \
|
Enum Shared::Global::fromInt(int src) \
|
||||||
|
@ -29,17 +29,6 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMimeType>
|
|
||||||
#include <QMimeDatabase>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QSize>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QLibrary>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
namespace Shared {
|
namespace Shared {
|
||||||
|
|
||||||
@ -47,19 +36,6 @@ namespace Shared {
|
|||||||
Q_DECLARE_TR_FUNCTIONS(Global)
|
Q_DECLARE_TR_FUNCTIONS(Global)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct FileInfo {
|
|
||||||
enum class Preview {
|
|
||||||
none,
|
|
||||||
picture,
|
|
||||||
movie
|
|
||||||
};
|
|
||||||
|
|
||||||
QString name;
|
|
||||||
QSize size;
|
|
||||||
QMimeType mime;
|
|
||||||
Preview preview;
|
|
||||||
};
|
|
||||||
|
|
||||||
Global();
|
Global();
|
||||||
|
|
||||||
static Global* getInstance();
|
static Global* getInstance();
|
||||||
@ -88,9 +64,6 @@ namespace Shared {
|
|||||||
|
|
||||||
static const std::set<QString> supportedImagesExts;
|
static const std::set<QString> supportedImagesExts;
|
||||||
|
|
||||||
static FileInfo getFileInfo(const QString& path);
|
|
||||||
static void highlightInFileManager(const QString& path);
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T fromInt(int src);
|
static T fromInt(int src);
|
||||||
|
|
||||||
@ -114,15 +87,6 @@ namespace Shared {
|
|||||||
static Global* instance;
|
static Global* instance;
|
||||||
|
|
||||||
std::map<QString, bool> pluginSupport;
|
std::map<QString, bool> pluginSupport;
|
||||||
std::map<QString, FileInfo> fileCache;
|
|
||||||
|
|
||||||
#ifdef WITH_KIO
|
|
||||||
static QLibrary openFileManagerWindowJob;
|
|
||||||
|
|
||||||
typedef void (*HighlightInFileManager)(const QUrl &);
|
|
||||||
|
|
||||||
static HighlightInFileManager hfm;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +170,6 @@ static const std::map<QString, std::pair<QString, QString>> icons = {
|
|||||||
{"favorite", {"favorite", "favorite"}},
|
{"favorite", {"favorite", "favorite"}},
|
||||||
{"unfavorite", {"draw-star", "unfavorite"}},
|
{"unfavorite", {"draw-star", "unfavorite"}},
|
||||||
{"list-add", {"list-add", "add"}},
|
{"list-add", {"list-add", "add"}},
|
||||||
{"folder", {"folder", "folder"}},
|
|
||||||
{"document-preview", {"document-preview", "document-preview"}}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +36,7 @@ Shared::Message::Message(Shared::Message::Type p_type):
|
|||||||
errorText(),
|
errorText(),
|
||||||
originalMessage(),
|
originalMessage(),
|
||||||
lastModified(),
|
lastModified(),
|
||||||
stanzaId(),
|
stanzaId()
|
||||||
attachPath()
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Shared::Message::Message():
|
Shared::Message::Message():
|
||||||
@ -57,8 +56,7 @@ Shared::Message::Message():
|
|||||||
errorText(),
|
errorText(),
|
||||||
originalMessage(),
|
originalMessage(),
|
||||||
lastModified(),
|
lastModified(),
|
||||||
stanzaId(),
|
stanzaId()
|
||||||
attachPath()
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QString Shared::Message::getBody() const
|
QString Shared::Message::getBody() const
|
||||||
@ -313,7 +311,6 @@ void Shared::Message::serialize(QDataStream& data) const
|
|||||||
data << lastModified;
|
data << lastModified;
|
||||||
}
|
}
|
||||||
data << stanzaId;
|
data << stanzaId;
|
||||||
data << attachPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shared::Message::deserialize(QDataStream& data)
|
void Shared::Message::deserialize(QDataStream& data)
|
||||||
@ -344,7 +341,6 @@ void Shared::Message::deserialize(QDataStream& data)
|
|||||||
data >> lastModified;
|
data >> lastModified;
|
||||||
}
|
}
|
||||||
data >> stanzaId;
|
data >> stanzaId;
|
||||||
data >> attachPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
||||||
@ -354,16 +350,6 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
|||||||
setState(static_cast<State>(itr.value().toUInt()));
|
setState(static_cast<State>(itr.value().toUInt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
itr = data.find("outOfBandUrl");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
setOutOfBandUrl(itr.value().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
itr = data.find("attachPath");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
setAttachPath(itr.value().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == State::error) {
|
if (state == State::error) {
|
||||||
itr = data.find("errorText");
|
itr = data.find("errorText");
|
||||||
if (itr != data.end()) {
|
if (itr != data.end()) {
|
||||||
@ -394,8 +380,6 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
|||||||
|
|
||||||
itr = data.find("body");
|
itr = data.find("body");
|
||||||
if (itr != data.end()) {
|
if (itr != data.end()) {
|
||||||
QString b = itr.value().toString();
|
|
||||||
if (body != b) {
|
|
||||||
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
||||||
QDateTime correctionDate;
|
QDateTime correctionDate;
|
||||||
if (dItr != data.end()) {
|
if (dItr != data.end()) {
|
||||||
@ -406,19 +390,10 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
|||||||
if (!edited || lastModified < correctionDate) {
|
if (!edited || lastModified < correctionDate) {
|
||||||
originalMessage = body;
|
originalMessage = body;
|
||||||
lastModified = correctionDate;
|
lastModified = correctionDate;
|
||||||
setBody(body);
|
setBody(itr.value().toString());
|
||||||
setEdited(true);
|
setEdited(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
|
||||||
if (dItr != data.end()) {
|
|
||||||
QDateTime ntime = dItr.value().toDateTime();
|
|
||||||
if (time != ntime) {
|
|
||||||
setTime(ntime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idChanged;
|
return idChanged;
|
||||||
}
|
}
|
||||||
@ -445,7 +420,7 @@ void Shared::Message::setOutOfBandUrl(const QString& url)
|
|||||||
|
|
||||||
bool Shared::Message::storable() const
|
bool Shared::Message::storable() const
|
||||||
{
|
{
|
||||||
return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0);
|
return id.size() > 0 && (body.size() > 0 || oob.size()) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shared::Message::setStanzaId(const QString& sid)
|
void Shared::Message::setStanzaId(const QString& sid)
|
||||||
@ -457,33 +432,3 @@ QString Shared::Message::getStanzaId() const
|
|||||||
{
|
{
|
||||||
return stanzaId;
|
return stanzaId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Shared::Message::getAttachPath() const
|
|
||||||
{
|
|
||||||
return attachPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setAttachPath(const QString& path)
|
|
||||||
{
|
|
||||||
attachPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message::Change::Change(const QMap<QString, QVariant>& _data):
|
|
||||||
data(_data),
|
|
||||||
idModified(false) {}
|
|
||||||
|
|
||||||
void Shared::Message::Change::operator()(Shared::Message& msg)
|
|
||||||
{
|
|
||||||
idModified = msg.change(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::Change::operator()(Shared::Message* msg)
|
|
||||||
{
|
|
||||||
idModified = msg->change(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::Change::hasIdBeenModified() const
|
|
||||||
{
|
|
||||||
return idModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -16,15 +16,15 @@
|
|||||||
* 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 SHAPER_MESSAGE_H
|
|
||||||
#define SHAPER_MESSAGE_H
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
|
|
||||||
|
#ifndef SHAPER_MESSAGE_H
|
||||||
|
#define SHAPER_MESSAGE_H
|
||||||
|
|
||||||
namespace Shared {
|
namespace Shared {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,22 +46,9 @@ public:
|
|||||||
delivered,
|
delivered,
|
||||||
error
|
error
|
||||||
};
|
};
|
||||||
|
|
||||||
static const State StateHighest = State::error;
|
static const State StateHighest = State::error;
|
||||||
static const State StateLowest = State::pending;
|
static const State StateLowest = State::pending;
|
||||||
|
|
||||||
struct Change //change functor, stores in idModified if ID has been modified during change
|
|
||||||
{
|
|
||||||
Change(const QMap<QString, QVariant>& _data);
|
|
||||||
void operator() (Message& msg);
|
|
||||||
void operator() (Message* msg);
|
|
||||||
bool hasIdBeenModified() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QMap<QString, QVariant>& data;
|
|
||||||
bool idModified;
|
|
||||||
};
|
|
||||||
|
|
||||||
Message(Type p_type);
|
Message(Type p_type);
|
||||||
Message();
|
Message();
|
||||||
|
|
||||||
@ -85,7 +72,6 @@ public:
|
|||||||
void setErrorText(const QString& err);
|
void setErrorText(const QString& err);
|
||||||
bool change(const QMap<QString, QVariant>& data);
|
bool change(const QMap<QString, QVariant>& data);
|
||||||
void setStanzaId(const QString& sid);
|
void setStanzaId(const QString& sid);
|
||||||
void setAttachPath(const QString& path);
|
|
||||||
|
|
||||||
QString getFrom() const;
|
QString getFrom() const;
|
||||||
QString getFromJid() const;
|
QString getFromJid() const;
|
||||||
@ -114,7 +100,6 @@ public:
|
|||||||
QDateTime getLastModified() const;
|
QDateTime getLastModified() const;
|
||||||
QString getOriginalBody() const;
|
QString getOriginalBody() const;
|
||||||
QString getStanzaId() const;
|
QString getStanzaId() const;
|
||||||
QString getAttachPath() const;
|
|
||||||
|
|
||||||
void serialize(QDataStream& data) const;
|
void serialize(QDataStream& data) const;
|
||||||
void deserialize(QDataStream& data);
|
void deserialize(QDataStream& data);
|
||||||
@ -138,7 +123,6 @@ private:
|
|||||||
QString originalMessage;
|
QString originalMessage;
|
||||||
QDateTime lastModified;
|
QDateTime lastModified;
|
||||||
QString stanzaId;
|
QString stanzaId;
|
||||||
QString attachPath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,45 +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 "messageinfo.h"
|
|
||||||
|
|
||||||
using namespace Shared;
|
|
||||||
|
|
||||||
Shared::MessageInfo::MessageInfo():
|
|
||||||
account(),
|
|
||||||
jid(),
|
|
||||||
messageId() {}
|
|
||||||
|
|
||||||
Shared::MessageInfo::MessageInfo(const QString& acc, const QString& j, const QString& id):
|
|
||||||
account(acc),
|
|
||||||
jid(j),
|
|
||||||
messageId(id) {}
|
|
||||||
|
|
||||||
Shared::MessageInfo::MessageInfo(const Shared::MessageInfo& other):
|
|
||||||
account(other.account),
|
|
||||||
jid(other.jid),
|
|
||||||
messageId(other.messageId) {}
|
|
||||||
|
|
||||||
Shared::MessageInfo & Shared::MessageInfo::operator=(const Shared::MessageInfo& other)
|
|
||||||
{
|
|
||||||
account = other.account;
|
|
||||||
jid = other.jid;
|
|
||||||
messageId = other.messageId;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
@ -1,43 +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 SHARED_MESSAGEINFO_H
|
|
||||||
#define SHARED_MESSAGEINFO_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace Shared {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
struct MessageInfo {
|
|
||||||
MessageInfo();
|
|
||||||
MessageInfo(const QString& acc, const QString& j, const QString& id);
|
|
||||||
MessageInfo(const MessageInfo& other);
|
|
||||||
|
|
||||||
QString account;
|
|
||||||
QString jid;
|
|
||||||
QString messageId;
|
|
||||||
|
|
||||||
MessageInfo& operator=(const MessageInfo& other);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SHARED_MESSAGEINFO_H
|
|
@ -20,12 +20,9 @@
|
|||||||
#define SHARED_UTILS_H
|
#define SHARED_UTILS_H
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
//#include "KIO/OpenFileManagerWindowJob"
|
|
||||||
|
|
||||||
#include <uuid/uuid.h>
|
#include <uuid/uuid.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.3)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(squawkUI)
|
project(squawkUI)
|
||||||
|
|
||||||
# Instruct CMake to run moc automatically when needed.
|
# Instruct CMake to run moc automatically when needed.
|
||||||
@ -7,13 +7,9 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
# Find the QtWidgets library
|
# Find the QtWidgets library
|
||||||
find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Core)
|
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||||
find_package(Boost 1.36.0 REQUIRED)
|
find_package(Qt5DBus CONFIG REQUIRED)
|
||||||
if(Boost_FOUND)
|
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(utils)
|
|
||||||
add_subdirectory(widgets)
|
add_subdirectory(widgets)
|
||||||
|
|
||||||
set(squawkUI_SRC
|
set(squawkUI_SRC
|
||||||
@ -29,15 +25,21 @@ set(squawkUI_SRC
|
|||||||
models/abstractparticipant.cpp
|
models/abstractparticipant.cpp
|
||||||
models/participant.cpp
|
models/participant.cpp
|
||||||
models/reference.cpp
|
models/reference.cpp
|
||||||
models/messagefeed.cpp
|
utils/messageline.cpp
|
||||||
models/element.cpp
|
utils//message.cpp
|
||||||
|
utils/resizer.cpp
|
||||||
|
utils/image.cpp
|
||||||
|
utils/flowlayout.cpp
|
||||||
|
utils/badge.cpp
|
||||||
|
utils/progress.cpp
|
||||||
|
utils/comboboxdelegate.cpp
|
||||||
|
utils/dropshadoweffect.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
# Tell CMake to create the helloworld executable
|
||||||
add_library(squawkUI STATIC ${squawkUI_SRC})
|
add_library(squawkUI ${squawkUI_SRC})
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
# Use the Widgets module from Qt 5.
|
||||||
target_link_libraries(squawkUI squawkWidgets)
|
target_link_libraries(squawkUI squawkWidgets)
|
||||||
target_link_libraries(squawkUI squawkUIUtils)
|
|
||||||
target_link_libraries(squawkUI Qt5::Widgets)
|
target_link_libraries(squawkUI Qt5::Widgets)
|
||||||
target_link_libraries(squawkUI Qt5::DBus)
|
target_link_libraries(squawkUI Qt5::DBus)
|
||||||
|
@ -231,7 +231,7 @@ void Models::Account::toOfflineState()
|
|||||||
Item::toOfflineState();
|
Item::toOfflineState();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Account::getAvatarPath() const
|
QString Models::Account::getAvatarPath()
|
||||||
{
|
{
|
||||||
return avatarPath;
|
return avatarPath;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ namespace Models {
|
|||||||
QString getError() const;
|
QString getError() const;
|
||||||
|
|
||||||
void setAvatarPath(const QString& path);
|
void setAvatarPath(const QString& path);
|
||||||
QString getAvatarPath() const;
|
QString getAvatarPath();
|
||||||
|
|
||||||
void setAvailability(Shared::Availability p_avail);
|
void setAvailability(Shared::Availability p_avail);
|
||||||
void setAvailability(unsigned int p_avail);
|
void setAvailability(unsigned int p_avail);
|
||||||
|
@ -17,26 +17,55 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "contact.h"
|
#include "contact.h"
|
||||||
|
#include "account.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap<QString, QVariant> &data, Item *parentItem):
|
Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap<QString, QVariant> &data, Item *parentItem):
|
||||||
Element(Item::contact, acc, p_jid, data, parentItem),
|
Item(Item::contact, data, parentItem),
|
||||||
|
jid(p_jid),
|
||||||
availability(Shared::Availability::offline),
|
availability(Shared::Availability::offline),
|
||||||
state(Shared::SubscriptionState::none),
|
state(Shared::SubscriptionState::none),
|
||||||
|
avatarState(Shared::Avatar::empty),
|
||||||
presences(),
|
presences(),
|
||||||
status()
|
messages(),
|
||||||
|
childMessages(0),
|
||||||
|
status(),
|
||||||
|
avatarPath(),
|
||||||
|
account(acc)
|
||||||
{
|
{
|
||||||
QMap<QString, QVariant>::const_iterator itr = data.find("state");
|
QMap<QString, QVariant>::const_iterator itr = data.find("state");
|
||||||
if (itr != data.end()) {
|
if (itr != data.end()) {
|
||||||
setState(itr.value().toUInt());
|
setState(itr.value().toUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itr = data.find("avatarState");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAvatarState(itr.value().toUInt());
|
||||||
|
}
|
||||||
|
itr = data.find("avatarPath");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAvatarPath(itr.value().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Contact::~Contact()
|
Models::Contact::~Contact()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Models::Contact::getJid() const
|
||||||
|
{
|
||||||
|
return jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::setJid(const QString p_jid)
|
||||||
|
{
|
||||||
|
if (jid != p_jid) {
|
||||||
|
jid = p_jid;
|
||||||
|
changed(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Contact::setAvailability(unsigned int p_state)
|
void Models::Contact::setAvailability(unsigned int p_state)
|
||||||
{
|
{
|
||||||
setAvailability(Shared::Global::fromInt<Shared::Availability>(p_state));
|
setAvailability(Shared::Global::fromInt<Shared::Availability>(p_state));
|
||||||
@ -115,12 +144,16 @@ void Models::Contact::update(const QString& field, const QVariant& value)
|
|||||||
{
|
{
|
||||||
if (field == "name") {
|
if (field == "name") {
|
||||||
setName(value.toString());
|
setName(value.toString());
|
||||||
|
} else if (field == "jid") {
|
||||||
|
setJid(value.toString());
|
||||||
} else if (field == "availability") {
|
} else if (field == "availability") {
|
||||||
setAvailability(value.toUInt());
|
setAvailability(value.toUInt());
|
||||||
} else if (field == "state") {
|
} else if (field == "state") {
|
||||||
setState(value.toUInt());
|
setState(value.toUInt());
|
||||||
} else {
|
} else if (field == "avatarState") {
|
||||||
Element::update(field, value);
|
setAvatarState(value.toUInt());
|
||||||
|
} else if (field == "avatarPath") {
|
||||||
|
setAvatarPath(value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,9 +192,11 @@ void Models::Contact::refresh()
|
|||||||
{
|
{
|
||||||
QDateTime lastActivity;
|
QDateTime lastActivity;
|
||||||
Presence* presence = 0;
|
Presence* presence = 0;
|
||||||
|
unsigned int count = 0;
|
||||||
for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
|
for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
|
||||||
Presence* pr = itr.value();
|
Presence* pr = itr.value();
|
||||||
QDateTime la = pr->getLastActivity();
|
QDateTime la = pr->getLastActivity();
|
||||||
|
count += pr->getMessagesCount();
|
||||||
|
|
||||||
if (la > lastActivity) {
|
if (la > lastActivity) {
|
||||||
lastActivity = la;
|
lastActivity = la;
|
||||||
@ -176,6 +211,11 @@ void Models::Contact::refresh()
|
|||||||
setAvailability(Shared::Availability::offline);
|
setAvailability(Shared::Availability::offline);
|
||||||
setStatus("");
|
setStatus("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (childMessages != count) {
|
||||||
|
childMessages = count;
|
||||||
|
changed(4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::Contact::_removeChild(int index)
|
void Models::Contact::_removeChild(int index)
|
||||||
@ -211,12 +251,87 @@ QIcon Models::Contact::getStatusIcon(bool big) const
|
|||||||
if (getMessagesCount() > 0) {
|
if (getMessagesCount() > 0) {
|
||||||
return Shared::icon("mail-message", big);
|
return Shared::icon("mail-message", big);
|
||||||
} else if (state == Shared::SubscriptionState::both || state == Shared::SubscriptionState::to) {
|
} else if (state == Shared::SubscriptionState::both || state == Shared::SubscriptionState::to) {
|
||||||
return Shared::availabilityIcon(availability, big);
|
return Shared::availabilityIcon(availability, big);;
|
||||||
} else {
|
} else {
|
||||||
return Shared::subscriptionStateIcon(state, big);
|
return Shared::subscriptionStateIcon(state, big);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Models::Contact::addMessage(const Shared::Message& data)
|
||||||
|
{
|
||||||
|
const QString& res = data.getPenPalResource();
|
||||||
|
if (res.size() > 0) {
|
||||||
|
QMap<QString, Presence*>::iterator itr = presences.find(res);
|
||||||
|
if (itr == presences.end()) {
|
||||||
|
// this is actually the place when I can spot someone's invisible presence, and there is nothing criminal in it, cuz the sender sent us a message
|
||||||
|
// therefore he have revealed himself
|
||||||
|
// the only issue is to find out when the sender is gone offline
|
||||||
|
Presence* pr = new Presence({});
|
||||||
|
pr->setName(res);
|
||||||
|
pr->setAvailability(Shared::Availability::invisible);
|
||||||
|
pr->setLastActivity(QDateTime::currentDateTimeUtc());
|
||||||
|
presences.insert(res, pr);
|
||||||
|
appendChild(pr);
|
||||||
|
pr->addMessage(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itr.value()->addMessage(data);
|
||||||
|
} else {
|
||||||
|
messages.emplace_back(data);
|
||||||
|
changed(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (Shared::Message& msg : messages) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
msg.change(data);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
for (Presence* pr : presences) {
|
||||||
|
found = pr->changeMessage(id, data);
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Models::Contact::getMessagesCount() const
|
||||||
|
{
|
||||||
|
return messages.size() + childMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::dropMessages()
|
||||||
|
{
|
||||||
|
if (messages.size() > 0) {
|
||||||
|
messages.clear();
|
||||||
|
changed(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
|
||||||
|
itr.value()->dropMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::getMessages(Models::Contact::Messages& container) const
|
||||||
|
{
|
||||||
|
for (Messages::const_iterator itr = messages.begin(), end = messages.end(); itr != end; ++itr) {
|
||||||
|
const Shared::Message& msg = *itr;
|
||||||
|
container.push_back(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QMap<QString, Presence*>::const_iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
|
||||||
|
itr.value()->getMessages(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Contact::toOfflineState()
|
void Models::Contact::toOfflineState()
|
||||||
{
|
{
|
||||||
std::deque<Item*>::size_type size = childItems.size();
|
std::deque<Item*>::size_type size = childItems.size();
|
||||||
@ -240,3 +355,75 @@ QString Models::Contact::getDisplayedName() const
|
|||||||
return getContactName();
|
return getContactName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::Contact::columnInvolvedInDisplay(int col)
|
||||||
|
{
|
||||||
|
return Item::columnInvolvedInDisplay(col) && col == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Models::Contact * Models::Contact::copy() const
|
||||||
|
{
|
||||||
|
Contact* cnt = new Contact(*this);
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Models::Contact::Contact(const Models::Contact& other):
|
||||||
|
Item(other),
|
||||||
|
jid(other.jid),
|
||||||
|
availability(other.availability),
|
||||||
|
state(other.state),
|
||||||
|
presences(),
|
||||||
|
messages(other.messages),
|
||||||
|
childMessages(0),
|
||||||
|
account(other.account)
|
||||||
|
{
|
||||||
|
for (const Presence* pres : other.presences) {
|
||||||
|
Presence* pCopy = new Presence(*pres);
|
||||||
|
presences.insert(pCopy->getName(), pCopy);
|
||||||
|
Item::appendChild(pCopy);
|
||||||
|
connect(pCopy, &Item::childChanged, this, &Contact::refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Models::Contact::getAvatarPath() const
|
||||||
|
{
|
||||||
|
return avatarPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Avatar Models::Contact::getAvatarState() const
|
||||||
|
{
|
||||||
|
return avatarState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::setAvatarPath(const QString& path)
|
||||||
|
{
|
||||||
|
if (path != avatarPath) {
|
||||||
|
avatarPath = path;
|
||||||
|
changed(7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::setAvatarState(Shared::Avatar p_state)
|
||||||
|
{
|
||||||
|
if (avatarState != p_state) {
|
||||||
|
avatarState = p_state;
|
||||||
|
changed(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Contact::setAvatarState(unsigned int p_state)
|
||||||
|
{
|
||||||
|
if (p_state <= static_cast<quint8>(Shared::Avatar::valid)) {
|
||||||
|
Shared::Avatar state = static_cast<Shared::Avatar>(p_state);
|
||||||
|
setAvatarState(state);
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to set invalid avatar state" << p_state << "to the contact" << jid << ", skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Models::Account * Models::Contact::getParentAccount() const
|
||||||
|
{
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#ifndef MODELS_CONTACT_H
|
#ifndef MODELS_CONTACT_H
|
||||||
#define MODELS_CONTACT_H
|
#define MODELS_CONTACT_H
|
||||||
|
|
||||||
#include "element.h"
|
#include "item.h"
|
||||||
#include "presence.h"
|
#include "presence.h"
|
||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
@ -31,34 +31,49 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
namespace Models {
|
namespace Models {
|
||||||
|
class Account;
|
||||||
|
|
||||||
class Contact : public Element
|
class Contact : public Item
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
typedef std::deque<Shared::Message> Messages;
|
||||||
Contact(const Account* acc, const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
Contact(const Account* acc, const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||||
|
Contact(const Contact& other);
|
||||||
~Contact();
|
~Contact();
|
||||||
|
|
||||||
|
QString getJid() const;
|
||||||
Shared::Availability getAvailability() const;
|
Shared::Availability getAvailability() const;
|
||||||
Shared::SubscriptionState getState() const;
|
Shared::SubscriptionState getState() const;
|
||||||
|
Shared::Avatar getAvatarState() const;
|
||||||
|
QString getAvatarPath() const;
|
||||||
QIcon getStatusIcon(bool big = false) const;
|
QIcon getStatusIcon(bool big = false) const;
|
||||||
|
|
||||||
int columnCount() const override;
|
int columnCount() const override;
|
||||||
QVariant data(int column) const override;
|
QVariant data(int column) const override;
|
||||||
|
|
||||||
void update(const QString& field, const QVariant& value) override;
|
void update(const QString& field, const QVariant& value);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
QString getContactName() const;
|
QString getContactName() const;
|
||||||
QString getStatus() const;
|
QString getStatus() const;
|
||||||
|
|
||||||
|
void addMessage(const Shared::Message& data);
|
||||||
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
unsigned int getMessagesCount() const;
|
||||||
|
void dropMessages();
|
||||||
|
void getMessages(Messages& container) const;
|
||||||
QString getDisplayedName() const override;
|
QString getDisplayedName() const override;
|
||||||
|
|
||||||
|
Contact* copy() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _removeChild(int index) override;
|
void _removeChild(int index) override;
|
||||||
void _appendChild(Models::Item * child) override;
|
void _appendChild(Models::Item * child) override;
|
||||||
|
bool columnInvolvedInDisplay(int col) override;
|
||||||
|
const Account* getParentAccount() const override;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void refresh();
|
void refresh();
|
||||||
@ -69,13 +84,23 @@ protected:
|
|||||||
void setAvailability(unsigned int p_state);
|
void setAvailability(unsigned int p_state);
|
||||||
void setState(Shared::SubscriptionState p_state);
|
void setState(Shared::SubscriptionState p_state);
|
||||||
void setState(unsigned int p_state);
|
void setState(unsigned int p_state);
|
||||||
|
void setAvatarState(Shared::Avatar p_state);
|
||||||
|
void setAvatarState(unsigned int p_state);
|
||||||
|
void setAvatarPath(const QString& path);
|
||||||
|
void setJid(const QString p_jid);
|
||||||
void setStatus(const QString& p_state);
|
void setStatus(const QString& p_state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString jid;
|
||||||
Shared::Availability availability;
|
Shared::Availability availability;
|
||||||
Shared::SubscriptionState state;
|
Shared::SubscriptionState state;
|
||||||
|
Shared::Avatar avatarState;
|
||||||
QMap<QString, Presence*> presences;
|
QMap<QString, Presence*> presences;
|
||||||
|
Messages messages;
|
||||||
|
unsigned int childMessages;
|
||||||
QString status;
|
QString status;
|
||||||
|
QString avatarPath;
|
||||||
|
const Account* account;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,184 +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 "element.h"
|
|
||||||
#include "account.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
Models::Element::Element(Type p_type, const Models::Account* acc, const QString& p_jid, const QMap<QString, QVariant>& data, Models::Item* parentItem):
|
|
||||||
Item(p_type, data, parentItem),
|
|
||||||
jid(p_jid),
|
|
||||||
avatarPath(),
|
|
||||||
avatarState(Shared::Avatar::empty),
|
|
||||||
account(acc),
|
|
||||||
feed(new MessageFeed(this))
|
|
||||||
{
|
|
||||||
connect(feed, &MessageFeed::requestArchive, this, &Element::requestArchive);
|
|
||||||
connect(feed, &MessageFeed::fileDownloadRequest, this, &Element::fileDownloadRequest);
|
|
||||||
connect(feed, &MessageFeed::unreadMessagesCountChanged, this, &Element::onFeedUnreadMessagesCountChanged);
|
|
||||||
connect(feed, &MessageFeed::unnoticedMessage, this, &Element::onFeedUnnoticedMessage);
|
|
||||||
connect(feed, &MessageFeed::localPathInvalid, this, &Element::localPathInvalid);
|
|
||||||
|
|
||||||
QMap<QString, QVariant>::const_iterator itr = data.find("avatarState");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
setAvatarState(itr.value().toUInt());
|
|
||||||
}
|
|
||||||
itr = data.find("avatarPath");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
setAvatarPath(itr.value().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Models::Element::~Element()
|
|
||||||
{
|
|
||||||
delete feed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString Models::Element::getJid() const
|
|
||||||
{
|
|
||||||
return jid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::setJid(const QString& p_jid)
|
|
||||||
{
|
|
||||||
if (jid != p_jid) {
|
|
||||||
jid = p_jid;
|
|
||||||
changed(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::update(const QString& field, const QVariant& value)
|
|
||||||
{
|
|
||||||
if (field == "jid") {
|
|
||||||
setJid(value.toString());
|
|
||||||
} else if (field == "avatarState") {
|
|
||||||
setAvatarState(value.toUInt());
|
|
||||||
} else if (field == "avatarPath") {
|
|
||||||
setAvatarPath(value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Models::Element::getAvatarPath() const
|
|
||||||
{
|
|
||||||
return avatarPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Avatar Models::Element::getAvatarState() const
|
|
||||||
{
|
|
||||||
return avatarState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::setAvatarPath(const QString& path)
|
|
||||||
{
|
|
||||||
if (path != avatarPath) {
|
|
||||||
avatarPath = path;
|
|
||||||
if (type == contact) {
|
|
||||||
changed(7);
|
|
||||||
} else if (type == room) {
|
|
||||||
changed(8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::setAvatarState(Shared::Avatar p_state)
|
|
||||||
{
|
|
||||||
if (avatarState != p_state) {
|
|
||||||
avatarState = p_state;
|
|
||||||
if (type == contact) {
|
|
||||||
changed(6);
|
|
||||||
} else if (type == room) {
|
|
||||||
changed(7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::setAvatarState(unsigned int p_state)
|
|
||||||
{
|
|
||||||
if (p_state <= static_cast<quint8>(Shared::Avatar::valid)) {
|
|
||||||
Shared::Avatar state = static_cast<Shared::Avatar>(p_state);
|
|
||||||
setAvatarState(state);
|
|
||||||
} else {
|
|
||||||
qDebug() << "An attempt to set invalid avatar state" << p_state << "to the element" << jid << ", skipping";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Models::Element::columnInvolvedInDisplay(int col)
|
|
||||||
{
|
|
||||||
return Item::columnInvolvedInDisplay(col) && col == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Models::Account * Models::Element::getParentAccount() const
|
|
||||||
{
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Models::Element::getMessagesCount() const
|
|
||||||
{
|
|
||||||
return feed->unreadMessagesCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::addMessage(const Shared::Message& data)
|
|
||||||
{
|
|
||||||
feed->addMessage(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
feed->changeMessage(id, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::responseArchive(const std::list<Shared::Message> list, bool last)
|
|
||||||
{
|
|
||||||
feed->responseArchive(list, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Models::Element::isRoom() const
|
|
||||||
{
|
|
||||||
return type != contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::fileProgress(const QString& messageId, qreal value, bool up)
|
|
||||||
{
|
|
||||||
feed->fileProgress(messageId, value, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::fileComplete(const QString& messageId, bool up)
|
|
||||||
{
|
|
||||||
feed->fileComplete(messageId, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::fileError(const QString& messageId, const QString& error, bool up)
|
|
||||||
{
|
|
||||||
feed->fileError(messageId, error, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::onFeedUnreadMessagesCountChanged()
|
|
||||||
{
|
|
||||||
if (type == contact) {
|
|
||||||
changed(4);
|
|
||||||
} else if (type == room) {
|
|
||||||
changed(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Element::onFeedUnnoticedMessage(const Shared::Message& msg)
|
|
||||||
{
|
|
||||||
emit unnoticedMessage(getAccountName(), msg);
|
|
||||||
}
|
|
@ -1,81 +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 ELEMENT_H
|
|
||||||
#define ELEMENT_H
|
|
||||||
|
|
||||||
#include "item.h"
|
|
||||||
#include "messagefeed.h"
|
|
||||||
|
|
||||||
namespace Models {
|
|
||||||
|
|
||||||
class Element : public Item
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
|
||||||
Element(Type p_type, const Account* acc, const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
|
||||||
~Element();
|
|
||||||
|
|
||||||
public:
|
|
||||||
QString getJid() const;
|
|
||||||
Shared::Avatar getAvatarState() const;
|
|
||||||
QString getAvatarPath() const;
|
|
||||||
|
|
||||||
virtual void update(const QString& field, const QVariant& value);
|
|
||||||
|
|
||||||
void addMessage(const Shared::Message& data);
|
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
|
||||||
unsigned int getMessagesCount() const;
|
|
||||||
void responseArchive(const std::list<Shared::Message> list, bool last);
|
|
||||||
bool isRoom() const;
|
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
|
||||||
void fileError(const QString& messageId, const QString& error, bool up);
|
|
||||||
void fileComplete(const QString& messageId, bool up);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void requestArchive(const QString& before);
|
|
||||||
void fileDownloadRequest(const QString& url);
|
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
|
||||||
void localPathInvalid(const QString& path);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setJid(const QString& p_jid);
|
|
||||||
void setAvatarState(Shared::Avatar p_state);
|
|
||||||
void setAvatarState(unsigned int p_state);
|
|
||||||
void setAvatarPath(const QString& path);
|
|
||||||
bool columnInvolvedInDisplay(int col) override;
|
|
||||||
const Account* getParentAccount() const override;
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void onFeedUnreadMessagesCountChanged();
|
|
||||||
void onFeedUnnoticedMessage(const Shared::Message& msg);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QString jid;
|
|
||||||
QString avatarPath;
|
|
||||||
Shared::Avatar avatarState;
|
|
||||||
|
|
||||||
const Account* account;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MessageFeed* feed;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ELEMENT_H
|
|
@ -283,15 +283,6 @@ Shared::ConnectionState Models::Item::getAccountConnectionState() const
|
|||||||
return acc->getState();
|
return acc->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Item::getAccountAvatarPath() const
|
|
||||||
{
|
|
||||||
const Account* acc = getParentAccount();
|
|
||||||
if (acc == nullptr) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return acc->getAvatarPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Models::Item::getDisplayedName() const
|
QString Models::Item::getDisplayedName() const
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
|
@ -80,7 +80,6 @@ class Item : public QObject{
|
|||||||
QString getAccountName() const;
|
QString getAccountName() const;
|
||||||
QString getAccountJid() const;
|
QString getAccountJid() const;
|
||||||
QString getAccountResource() const;
|
QString getAccountResource() const;
|
||||||
QString getAccountAvatarPath() const;
|
|
||||||
Shared::ConnectionState getAccountConnectionState() const;
|
Shared::ConnectionState getAccountConnectionState() const;
|
||||||
Shared::Availability getAccountAvailability() const;
|
Shared::Availability getAccountAvailability() const;
|
||||||
|
|
||||||
|
@ -1,576 +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 "messagefeed.h"
|
|
||||||
#include "element.h"
|
|
||||||
#include "room.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
const QHash<int, QByteArray> Models::MessageFeed::roles = {
|
|
||||||
{Text, "text"},
|
|
||||||
{Sender, "sender"},
|
|
||||||
{Date, "date"},
|
|
||||||
{DeliveryState, "deliveryState"},
|
|
||||||
{Correction, "correction"},
|
|
||||||
{SentByMe,"sentByMe"},
|
|
||||||
{Avatar, "avatar"},
|
|
||||||
{Attach, "attach"},
|
|
||||||
{Id, "id"},
|
|
||||||
{Error, "error"},
|
|
||||||
{Bulk, "bulk"}
|
|
||||||
};
|
|
||||||
|
|
||||||
Models::MessageFeed::MessageFeed(const Element* ri, QObject* parent):
|
|
||||||
QAbstractListModel(parent),
|
|
||||||
storage(),
|
|
||||||
indexById(storage.get<id>()),
|
|
||||||
indexByTime(storage.get<time>()),
|
|
||||||
rosterItem(ri),
|
|
||||||
syncState(incomplete),
|
|
||||||
uploads(),
|
|
||||||
downloads(),
|
|
||||||
unreadMessages(new std::set<QString>()),
|
|
||||||
observersAmount(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Models::MessageFeed::~MessageFeed()
|
|
||||||
{
|
|
||||||
delete unreadMessages;
|
|
||||||
|
|
||||||
for (Shared::Message* message : storage) {
|
|
||||||
delete message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::addMessage(const Shared::Message& msg)
|
|
||||||
{
|
|
||||||
QString id = msg.getId();
|
|
||||||
StorageById::const_iterator itr = indexById.find(id);
|
|
||||||
if (itr != indexById.end()) {
|
|
||||||
qDebug() << "received more then one message with the same id, skipping yet the new one";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message* copy = new Shared::Message(msg);
|
|
||||||
StorageByTime::const_iterator tItr = indexByTime.upper_bound(msg.getTime());
|
|
||||||
int position;
|
|
||||||
if (tItr == indexByTime.end()) {
|
|
||||||
position = storage.size();
|
|
||||||
} else {
|
|
||||||
position = indexByTime.rank(tItr);
|
|
||||||
}
|
|
||||||
beginInsertRows(QModelIndex(), position, position);
|
|
||||||
storage.insert(copy);
|
|
||||||
endInsertRows();
|
|
||||||
|
|
||||||
emit newMessage(msg);
|
|
||||||
|
|
||||||
if (observersAmount == 0 && !msg.getForwarded()) { //not to notify when the message is delivered by the carbon copy
|
|
||||||
unreadMessages->insert(msg.getId()); //cuz it could be my own one or the one I read on another device
|
|
||||||
emit unreadMessagesCountChanged();
|
|
||||||
emit unnoticedMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
StorageById::iterator itr = indexById.find(id);
|
|
||||||
if (itr == indexById.end()) {
|
|
||||||
qDebug() << "received a command to change a message, but the message couldn't be found, skipping";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message* msg = *itr;
|
|
||||||
std::set<MessageRoles> changeRoles = detectChanges(*msg, data);
|
|
||||||
QModelIndex index = modelIndexByTime(id, msg->getTime());
|
|
||||||
Shared::Message::Change functor(data);
|
|
||||||
bool success = indexById.modify(itr, functor);
|
|
||||||
if (!success) {
|
|
||||||
qDebug() << "received a command to change a message, but something went wrong modifying message in the feed, throwing error";
|
|
||||||
throw 872;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (functor.hasIdBeenModified()) {
|
|
||||||
changeRoles.insert(MessageRoles::Id);
|
|
||||||
std::set<QString>::const_iterator umi = unreadMessages->find(id);
|
|
||||||
if (umi != unreadMessages->end()) {
|
|
||||||
unreadMessages->erase(umi);
|
|
||||||
unreadMessages->insert(msg->getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeRoles.size() > 0) {
|
|
||||||
//change message is a final event in download/upload event train
|
|
||||||
//only after changeMessage we can consider the download is done
|
|
||||||
Progress::const_iterator dItr = downloads.find(id);
|
|
||||||
bool attachOrError = changeRoles.count(MessageRoles::Attach) > 0 || changeRoles.count(MessageRoles::Error);
|
|
||||||
if (dItr != downloads.end()) {
|
|
||||||
if (attachOrError) {
|
|
||||||
downloads.erase(dItr);
|
|
||||||
} else if (changeRoles.count(MessageRoles::Id) > 0) {
|
|
||||||
qreal progress = dItr->second;
|
|
||||||
downloads.erase(dItr);
|
|
||||||
downloads.insert(std::make_pair(msg->getId(), progress));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dItr = uploads.find(id);
|
|
||||||
if (dItr != uploads.end()) {
|
|
||||||
if (attachOrError) {
|
|
||||||
uploads.erase(dItr);
|
|
||||||
} else if (changeRoles.count(MessageRoles::Id) > 0) {
|
|
||||||
qreal progress = dItr->second;
|
|
||||||
uploads.erase(dItr);
|
|
||||||
uploads.insert(std::make_pair(msg->getId(), progress));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<int> cr;
|
|
||||||
for (MessageRoles role : changeRoles) {
|
|
||||||
cr.push_back(role);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit dataChanged(index, index, cr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<Models::MessageFeed::MessageRoles> Models::MessageFeed::detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const
|
|
||||||
{
|
|
||||||
std::set<MessageRoles> roles;
|
|
||||||
Shared::Message::State state = msg.getState();
|
|
||||||
QMap<QString, QVariant>::const_iterator itr = data.find("state");
|
|
||||||
if (itr != data.end() && static_cast<Shared::Message::State>(itr.value().toUInt()) != state) {
|
|
||||||
roles.insert(MessageRoles::DeliveryState);
|
|
||||||
}
|
|
||||||
|
|
||||||
itr = data.find("outOfBandUrl");
|
|
||||||
bool att = false;
|
|
||||||
if (itr != data.end() && itr.value().toString() != msg.getOutOfBandUrl()) {
|
|
||||||
roles.insert(MessageRoles::Attach);
|
|
||||||
att = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!att) {
|
|
||||||
itr = data.find("attachPath");
|
|
||||||
if (itr != data.end() && itr.value().toString() != msg.getAttachPath()) {
|
|
||||||
roles.insert(MessageRoles::Attach);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == Shared::Message::State::error) {
|
|
||||||
itr = data.find("errorText");
|
|
||||||
if (itr != data.end() && itr.value().toString() != msg.getErrorText()) {
|
|
||||||
roles.insert(MessageRoles::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
itr = data.find("body");
|
|
||||||
if (itr != data.end() && itr.value().toString() != msg.getBody()) {
|
|
||||||
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
|
||||||
QDateTime correctionDate;
|
|
||||||
if (dItr != data.end()) {
|
|
||||||
correctionDate = dItr.value().toDateTime();
|
|
||||||
} else {
|
|
||||||
correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied
|
|
||||||
}
|
|
||||||
if (!msg.getEdited() || msg.getLastModified() < correctionDate) {
|
|
||||||
roles.insert(MessageRoles::Text);
|
|
||||||
roles.insert(MessageRoles::Correction);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
|
||||||
if (dItr != data.end()) {
|
|
||||||
QDateTime ntime = dItr.value().toDateTime();
|
|
||||||
if (msg.getTime() != ntime) {
|
|
||||||
roles.insert(MessageRoles::Date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::removeMessage(const QString& id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
|
||||||
{
|
|
||||||
int i = index.row();
|
|
||||||
QVariant answer;
|
|
||||||
|
|
||||||
StorageByTime::const_iterator itr = indexByTime.nth(i);
|
|
||||||
if (itr != indexByTime.end()) {
|
|
||||||
const Shared::Message* msg = *itr;
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
case Text: {
|
|
||||||
QString body = msg->getBody();
|
|
||||||
if (body != msg->getOutOfBandUrl()) {
|
|
||||||
answer = body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Sender:
|
|
||||||
if (sentByMe(*msg)) {
|
|
||||||
answer = rosterItem->getAccountName();
|
|
||||||
} else {
|
|
||||||
if (rosterItem->isRoom()) {
|
|
||||||
answer = msg->getFromResource();
|
|
||||||
} else {
|
|
||||||
answer = rosterItem->getDisplayedName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Date:
|
|
||||||
answer = msg->getTime();
|
|
||||||
break;
|
|
||||||
case DeliveryState:
|
|
||||||
answer = static_cast<unsigned int>(msg->getState());
|
|
||||||
break;
|
|
||||||
case Correction:
|
|
||||||
answer = msg->getEdited();
|
|
||||||
break;
|
|
||||||
case SentByMe:
|
|
||||||
answer = sentByMe(*msg);
|
|
||||||
break;
|
|
||||||
case Avatar: {
|
|
||||||
QString path;
|
|
||||||
if (sentByMe(*msg)) {
|
|
||||||
path = rosterItem->getAccountAvatarPath();
|
|
||||||
} else if (!rosterItem->isRoom()) {
|
|
||||||
if (rosterItem->getAvatarState() != Shared::Avatar::empty) {
|
|
||||||
path = rosterItem->getAvatarPath();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const Room* room = static_cast<const Room*>(rosterItem);
|
|
||||||
path = room->getParticipantIconPath(msg->getFromResource());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.size() == 0) {
|
|
||||||
answer = Shared::iconPath("user", true);
|
|
||||||
} else {
|
|
||||||
answer = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Attach:
|
|
||||||
answer.setValue(fillAttach(*msg));
|
|
||||||
break;
|
|
||||||
case Id:
|
|
||||||
answer.setValue(msg->getId());
|
|
||||||
break;
|
|
||||||
break;
|
|
||||||
case Error:
|
|
||||||
answer.setValue(msg->getErrorText());
|
|
||||||
break;
|
|
||||||
case Bulk: {
|
|
||||||
FeedItem item;
|
|
||||||
item.id = msg->getId();
|
|
||||||
|
|
||||||
std::set<QString>::const_iterator umi = unreadMessages->find(item.id);
|
|
||||||
if (umi != unreadMessages->end()) {
|
|
||||||
unreadMessages->erase(umi);
|
|
||||||
emit unreadMessagesCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.sentByMe = sentByMe(*msg);
|
|
||||||
item.date = msg->getTime();
|
|
||||||
item.state = msg->getState();
|
|
||||||
item.error = msg->getErrorText();
|
|
||||||
item.correction = msg->getEdited();
|
|
||||||
|
|
||||||
QString body = msg->getBody();
|
|
||||||
if (body != msg->getOutOfBandUrl()) {
|
|
||||||
item.text = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.avatar.clear();
|
|
||||||
if (item.sentByMe) {
|
|
||||||
item.sender = rosterItem->getAccountName();
|
|
||||||
item.avatar = rosterItem->getAccountAvatarPath();
|
|
||||||
} else {
|
|
||||||
if (rosterItem->isRoom()) {
|
|
||||||
item.sender = msg->getFromResource();
|
|
||||||
const Room* room = static_cast<const Room*>(rosterItem);
|
|
||||||
item.avatar = room->getParticipantIconPath(msg->getFromResource());
|
|
||||||
} else {
|
|
||||||
item.sender = rosterItem->getDisplayedName();
|
|
||||||
if (rosterItem->getAvatarState() != Shared::Avatar::empty) {
|
|
||||||
item.avatar = rosterItem->getAvatarPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.avatar.size() == 0) {
|
|
||||||
item.avatar = Shared::iconPath("user", true);
|
|
||||||
}
|
|
||||||
item.attach = fillAttach(*msg);
|
|
||||||
answer.setValue(item);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Models::MessageFeed::rowCount(const QModelIndex& parent) const
|
|
||||||
{
|
|
||||||
return storage.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
|
||||||
{
|
|
||||||
return unreadMessages->size();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Models::MessageFeed::canFetchMore(const QModelIndex& parent) const
|
|
||||||
{
|
|
||||||
return syncState == incomplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::fetchMore(const QModelIndex& parent)
|
|
||||||
{
|
|
||||||
if (syncState == incomplete) {
|
|
||||||
syncState = syncing;
|
|
||||||
emit syncStateChange(syncState);
|
|
||||||
emit requestStateChange(true);
|
|
||||||
|
|
||||||
if (storage.size() == 0) {
|
|
||||||
emit requestArchive("");
|
|
||||||
} else {
|
|
||||||
emit requestArchive((*indexByTime.rbegin())->getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::responseArchive(const std::list<Shared::Message> list, bool last)
|
|
||||||
{
|
|
||||||
Storage::size_type size = storage.size();
|
|
||||||
|
|
||||||
beginInsertRows(QModelIndex(), size, size + list.size() - 1);
|
|
||||||
for (const Shared::Message& msg : list) {
|
|
||||||
Shared::Message* copy = new Shared::Message(msg);
|
|
||||||
storage.insert(copy);
|
|
||||||
}
|
|
||||||
endInsertRows();
|
|
||||||
|
|
||||||
if (syncState == syncing) {
|
|
||||||
if (last) {
|
|
||||||
syncState = complete;
|
|
||||||
} else {
|
|
||||||
syncState = incomplete;
|
|
||||||
}
|
|
||||||
emit syncStateChange(syncState);
|
|
||||||
emit requestStateChange(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& parent) const
|
|
||||||
{
|
|
||||||
if (!hasIndex(row, column, parent)) {
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageByTime::iterator itr = indexByTime.nth(row);
|
|
||||||
if (itr != indexByTime.end()) {
|
|
||||||
Shared::Message* msg = *itr;
|
|
||||||
|
|
||||||
return createIndex(row, column, msg);
|
|
||||||
} else {
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> Models::MessageFeed::roleNames() const
|
|
||||||
{
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const
|
|
||||||
{
|
|
||||||
if (rosterItem->isRoom()) {
|
|
||||||
const Room* room = static_cast<const Room*>(rosterItem);
|
|
||||||
return room->getNick().toLower() == msg.getFromResource().toLower();
|
|
||||||
} else {
|
|
||||||
return msg.getOutgoing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const
|
|
||||||
{
|
|
||||||
::Models::Attachment att;
|
|
||||||
|
|
||||||
att.localPath = msg.getAttachPath();
|
|
||||||
att.remotePath = msg.getOutOfBandUrl();
|
|
||||||
|
|
||||||
if (att.remotePath.size() == 0) {
|
|
||||||
if (att.localPath.size() == 0) {
|
|
||||||
att.state = none;
|
|
||||||
} else {
|
|
||||||
Progress::const_iterator itr = uploads.find(msg.getId());
|
|
||||||
if (itr == uploads.end()) {
|
|
||||||
att.state = local;
|
|
||||||
} else {
|
|
||||||
att.state = uploading;
|
|
||||||
att.progress = itr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (att.localPath.size() == 0) {
|
|
||||||
Progress::const_iterator itr = downloads.find(msg.getId());
|
|
||||||
if (itr == downloads.end()) {
|
|
||||||
att.state = remote;
|
|
||||||
} else {
|
|
||||||
att.state = downloading;
|
|
||||||
att.progress = itr->second;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
att.state = ready;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return att;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::downloadAttachment(const QString& messageId)
|
|
||||||
{
|
|
||||||
QModelIndex ind = modelIndexById(messageId);
|
|
||||||
if (ind.isValid()) {
|
|
||||||
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
|
|
||||||
Shared::Message* msg = static_cast<Shared::Message*>(ind.internalPointer());
|
|
||||||
emit dataChanged(ind, ind, {MessageRoles::Attach});
|
|
||||||
emit fileDownloadRequest(msg->getOutOfBandUrl());
|
|
||||||
} else {
|
|
||||||
qDebug() << "Attachment download for message with id" << messageId << "is already in progress, skipping";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "An attempt to download an attachment for the message that doesn't exist. ID:" << messageId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::uploadAttachment(const QString& messageId)
|
|
||||||
{
|
|
||||||
qDebug() << "request to upload attachment of the message" << messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Models::MessageFeed::registerUpload(const QString& messageId)
|
|
||||||
{
|
|
||||||
return uploads.insert(std::make_pair(messageId, 0)).second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up)
|
|
||||||
{
|
|
||||||
Progress* pr = 0;
|
|
||||||
if (up) {
|
|
||||||
pr = &uploads;
|
|
||||||
} else {
|
|
||||||
pr = &downloads;
|
|
||||||
}
|
|
||||||
|
|
||||||
Progress::iterator itr = pr->find(messageId);
|
|
||||||
if (itr != pr->end()) {
|
|
||||||
itr->second = value;
|
|
||||||
QModelIndex ind = modelIndexById(messageId);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::fileComplete(const QString& messageId, bool up)
|
|
||||||
{
|
|
||||||
fileProgress(messageId, 1, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up)
|
|
||||||
{
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::incrementObservers()
|
|
||||||
{
|
|
||||||
++observersAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::decrementObservers()
|
|
||||||
{
|
|
||||||
--observersAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const
|
|
||||||
{
|
|
||||||
StorageById::const_iterator itr = indexById.find(id);
|
|
||||||
if (itr != indexById.end()) {
|
|
||||||
Shared::Message* msg = *itr;
|
|
||||||
return modelIndexByTime(id, msg->getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const
|
|
||||||
{
|
|
||||||
StorageByTime::const_iterator tItr = indexByTime.upper_bound(time);
|
|
||||||
StorageByTime::const_iterator tBeg = indexByTime.begin();
|
|
||||||
bool found = false;
|
|
||||||
while (tItr != tBeg) {
|
|
||||||
if (id == (*tItr)->getId()) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--tItr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found || id == (*tItr)->getId()) {
|
|
||||||
int position = indexByTime.rank(tItr);
|
|
||||||
return createIndex(position, 0, *tItr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId)
|
|
||||||
{
|
|
||||||
StorageById::iterator itr = indexById.find(messageId);
|
|
||||||
if (itr == indexById.end()) {
|
|
||||||
qDebug() << "received a command to change a message, but the message couldn't be found, skipping";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message* msg = *itr;
|
|
||||||
|
|
||||||
emit localPathInvalid(msg->getAttachPath());
|
|
||||||
|
|
||||||
//gonna change the message in current model right away, to prevent spam on each attemt to draw element
|
|
||||||
QModelIndex index = modelIndexByTime(messageId, msg->getTime());
|
|
||||||
msg->setAttachPath("");
|
|
||||||
|
|
||||||
emit dataChanged(index, index, {MessageRoles::Attach});
|
|
||||||
}
|
|
||||||
|
|
||||||
Models::MessageFeed::SyncState Models::MessageFeed::getSyncState() const
|
|
||||||
{
|
|
||||||
return syncState;
|
|
||||||
}
|
|
@ -1,195 +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 MESSAGEFEED_H
|
|
||||||
#define MESSAGEFEED_H
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <boost/multi_index_container.hpp>
|
|
||||||
#include <boost/multi_index/ordered_index.hpp>
|
|
||||||
#include <boost/multi_index/ranked_index.hpp>
|
|
||||||
#include <boost/multi_index/mem_fun.hpp>
|
|
||||||
|
|
||||||
#include <shared/message.h>
|
|
||||||
#include <shared/icons.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Models {
|
|
||||||
class Element;
|
|
||||||
struct Attachment;
|
|
||||||
|
|
||||||
class MessageFeed : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
enum SyncState {
|
|
||||||
incomplete,
|
|
||||||
syncing,
|
|
||||||
complete
|
|
||||||
};
|
|
||||||
|
|
||||||
MessageFeed(const Element* rosterItem, QObject *parent = nullptr);
|
|
||||||
~MessageFeed();
|
|
||||||
|
|
||||||
void addMessage(const Shared::Message& msg);
|
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
|
||||||
void removeMessage(const QString& id);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
bool canFetchMore(const QModelIndex & parent) const override;
|
|
||||||
void fetchMore(const QModelIndex & parent) override;
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex & parent) const override;
|
|
||||||
|
|
||||||
void responseArchive(const std::list<Shared::Message> list, bool last);
|
|
||||||
void downloadAttachment(const QString& messageId);
|
|
||||||
void uploadAttachment(const QString& messageId);
|
|
||||||
bool registerUpload(const QString& messageId);
|
|
||||||
void reportLocalPathInvalid(const QString& messageId);
|
|
||||||
|
|
||||||
unsigned int unreadMessagesCount() const;
|
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
|
||||||
void fileError(const QString& messageId, const QString& error, bool up);
|
|
||||||
void fileComplete(const QString& messageId, bool up);
|
|
||||||
|
|
||||||
void incrementObservers();
|
|
||||||
void decrementObservers();
|
|
||||||
SyncState getSyncState() const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void requestArchive(const QString& before);
|
|
||||||
void requestStateChange(bool requesting);
|
|
||||||
void fileDownloadRequest(const QString& url);
|
|
||||||
void unreadMessagesCountChanged();
|
|
||||||
void newMessage(const Shared::Message& msg);
|
|
||||||
void unnoticedMessage(const Shared::Message& msg);
|
|
||||||
void localPathInvalid(const QString& path);
|
|
||||||
void syncStateChange(SyncState state);
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum MessageRoles {
|
|
||||||
Text = Qt::UserRole + 1,
|
|
||||||
Sender,
|
|
||||||
Date,
|
|
||||||
DeliveryState,
|
|
||||||
Correction,
|
|
||||||
SentByMe,
|
|
||||||
Avatar,
|
|
||||||
Attach,
|
|
||||||
Id,
|
|
||||||
Error,
|
|
||||||
Bulk
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool sentByMe(const Shared::Message& msg) const;
|
|
||||||
Attachment fillAttach(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;
|
|
||||||
|
|
||||||
private:
|
|
||||||
//tags
|
|
||||||
struct id {};
|
|
||||||
struct time {};
|
|
||||||
|
|
||||||
typedef boost::multi_index_container<
|
|
||||||
Shared::Message*,
|
|
||||||
boost::multi_index::indexed_by<
|
|
||||||
boost::multi_index::ordered_unique<
|
|
||||||
boost::multi_index::tag<id>,
|
|
||||||
boost::multi_index::const_mem_fun<
|
|
||||||
Shared::Message,
|
|
||||||
QString,
|
|
||||||
&Shared::Message::getId
|
|
||||||
>
|
|
||||||
>,
|
|
||||||
boost::multi_index::ranked_non_unique<
|
|
||||||
boost::multi_index::tag<time>,
|
|
||||||
boost::multi_index::const_mem_fun<
|
|
||||||
Shared::Message,
|
|
||||||
QDateTime,
|
|
||||||
&Shared::Message::getTime
|
|
||||||
>,
|
|
||||||
std::greater<QDateTime>
|
|
||||||
>
|
|
||||||
>
|
|
||||||
> Storage;
|
|
||||||
|
|
||||||
typedef Storage::index<id>::type StorageById;
|
|
||||||
typedef Storage::index<time>::type StorageByTime;
|
|
||||||
Storage storage;
|
|
||||||
StorageById& indexById;
|
|
||||||
StorageByTime& indexByTime;
|
|
||||||
|
|
||||||
const Element* rosterItem;
|
|
||||||
SyncState syncState;
|
|
||||||
|
|
||||||
typedef std::map<QString, qreal> Progress;
|
|
||||||
Progress uploads;
|
|
||||||
Progress downloads;
|
|
||||||
|
|
||||||
std::set<QString>* unreadMessages;
|
|
||||||
uint16_t observersAmount;
|
|
||||||
|
|
||||||
static const QHash<int, QByteArray> roles;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AttachmentType {
|
|
||||||
none,
|
|
||||||
remote,
|
|
||||||
local,
|
|
||||||
downloading,
|
|
||||||
uploading,
|
|
||||||
errorDownload,
|
|
||||||
errorUpload,
|
|
||||||
ready
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Attachment {
|
|
||||||
AttachmentType state;
|
|
||||||
qreal progress;
|
|
||||||
QString localPath;
|
|
||||||
QString remotePath;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FeedItem {
|
|
||||||
QString id;
|
|
||||||
QString text;
|
|
||||||
QString sender;
|
|
||||||
QString avatar;
|
|
||||||
QString error;
|
|
||||||
bool sentByMe;
|
|
||||||
bool correction;
|
|
||||||
QDateTime date;
|
|
||||||
Shared::Message::State state;
|
|
||||||
Attachment attach;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Models::Attachment)
|
|
||||||
Q_DECLARE_METATYPE(Models::FeedItem)
|
|
||||||
|
|
||||||
#endif // MESSAGEFEED_H
|
|
@ -20,15 +20,82 @@
|
|||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
|
|
||||||
Models::Presence::Presence(const QMap<QString, QVariant>& data, Item* parentItem):
|
Models::Presence::Presence(const QMap<QString, QVariant>& data, Item* parentItem):
|
||||||
AbstractParticipant(Item::presence, data, parentItem)
|
AbstractParticipant(Item::presence, data, parentItem),
|
||||||
|
messages()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Presence::Presence(const Models::Presence& other):
|
||||||
|
AbstractParticipant(other),
|
||||||
|
messages(other.messages)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Models::Presence::~Presence()
|
Models::Presence::~Presence()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Models::Presence::columnCount() const
|
int Models::Presence::columnCount() const
|
||||||
{
|
{
|
||||||
return 4;
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Models::Presence::data(int column) const
|
||||||
|
{
|
||||||
|
switch (column) {
|
||||||
|
case 4:
|
||||||
|
return getMessagesCount();
|
||||||
|
default:
|
||||||
|
return AbstractParticipant::data(column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Models::Presence::getMessagesCount() const
|
||||||
|
{
|
||||||
|
return messages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Presence::addMessage(const Shared::Message& data)
|
||||||
|
{
|
||||||
|
messages.emplace_back(data);
|
||||||
|
changed(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Models::Presence::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (Shared::Message& msg : messages) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
msg.change(data);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Presence::dropMessages()
|
||||||
|
{
|
||||||
|
if (messages.size() > 0) {
|
||||||
|
messages.clear();
|
||||||
|
changed(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Models::Presence::getStatusIcon(bool big) const
|
||||||
|
{
|
||||||
|
if (getMessagesCount() > 0) {
|
||||||
|
return Shared::icon("mail-message", big);
|
||||||
|
} else {
|
||||||
|
return AbstractParticipant::getStatusIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Presence::getMessages(Models::Presence::Messages& container) const
|
||||||
|
{
|
||||||
|
for (Messages::const_iterator itr = messages.begin(), end = messages.end(); itr != end; ++itr) {
|
||||||
|
const Shared::Message& msg = *itr;
|
||||||
|
container.push_back(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,25 @@ class Presence : public Models::AbstractParticipant
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
typedef std::deque<Shared::Message> Messages;
|
||||||
explicit Presence(const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
explicit Presence(const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||||
|
Presence(const Presence& other);
|
||||||
~Presence();
|
~Presence();
|
||||||
|
|
||||||
int columnCount() const override;
|
int columnCount() const override;
|
||||||
|
QVariant data(int column) const override;
|
||||||
|
|
||||||
|
QIcon getStatusIcon(bool big = false) const override;
|
||||||
|
|
||||||
|
unsigned int getMessagesCount() const;
|
||||||
|
void dropMessages();
|
||||||
|
void addMessage(const Shared::Message& data);
|
||||||
|
bool changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
void getMessages(Messages& container) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Messages messages;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,16 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
Models::Room::Room(const Account* acc, const QString& p_jid, const QMap<QString, QVariant>& data, Models::Item* parentItem):
|
Models::Room::Room(const QString& p_jid, const QMap<QString, QVariant>& data, Models::Item* parentItem):
|
||||||
Element(room, acc, p_jid, data, parentItem),
|
Item(room, data, parentItem),
|
||||||
autoJoin(false),
|
autoJoin(false),
|
||||||
joined(false),
|
joined(false),
|
||||||
|
jid(p_jid),
|
||||||
nick(""),
|
nick(""),
|
||||||
subject(""),
|
subject(""),
|
||||||
|
avatarState(Shared::Avatar::empty),
|
||||||
|
avatarPath(""),
|
||||||
|
messages(),
|
||||||
participants(),
|
participants(),
|
||||||
exParticipantAvatars()
|
exParticipantAvatars()
|
||||||
{
|
{
|
||||||
@ -51,6 +55,16 @@ Models::Room::Room(const Account* acc, const QString& p_jid, const QMap<QString,
|
|||||||
setSubject(itr.value().toString());
|
setSubject(itr.value().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itr = data.find("avatarState");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAvatarState(itr.value().toUInt());
|
||||||
|
}
|
||||||
|
itr = data.find("avatarPath");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAvatarPath(itr.value().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
itr = data.find("avatars");
|
itr = data.find("avatars");
|
||||||
if (itr != data.end()) {
|
if (itr != data.end()) {
|
||||||
QMap<QString, QVariant> avs = itr.value().toMap();
|
QMap<QString, QVariant> avs = itr.value().toMap();
|
||||||
@ -64,11 +78,21 @@ Models::Room::~Room()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int Models::Room::getUnreadMessagesCount() const
|
||||||
|
{
|
||||||
|
return messages.size();
|
||||||
|
}
|
||||||
|
|
||||||
int Models::Room::columnCount() const
|
int Models::Room::columnCount() const
|
||||||
{
|
{
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Models::Room::getJid() const
|
||||||
|
{
|
||||||
|
return jid;
|
||||||
|
}
|
||||||
|
|
||||||
bool Models::Room::getAutoJoin() const
|
bool Models::Room::getAutoJoin() const
|
||||||
{
|
{
|
||||||
return autoJoin;
|
return autoJoin;
|
||||||
@ -127,6 +151,14 @@ void Models::Room::setAutoJoin(bool p_autoJoin)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Models::Room::setJid(const QString& p_jid)
|
||||||
|
{
|
||||||
|
if (jid != p_jid) {
|
||||||
|
jid = p_jid;
|
||||||
|
changed(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Room::setJoined(bool p_joined)
|
void Models::Room::setJoined(bool p_joined)
|
||||||
{
|
{
|
||||||
if (joined != p_joined) {
|
if (joined != p_joined) {
|
||||||
@ -150,6 +182,8 @@ void Models::Room::update(const QString& field, const QVariant& value)
|
|||||||
{
|
{
|
||||||
if (field == "name") {
|
if (field == "name") {
|
||||||
setName(value.toString());
|
setName(value.toString());
|
||||||
|
} else if (field == "jid") {
|
||||||
|
setJid(value.toString());
|
||||||
} else if (field == "joined") {
|
} else if (field == "joined") {
|
||||||
setJoined(value.toBool());
|
setJoined(value.toBool());
|
||||||
} else if (field == "autoJoin") {
|
} else if (field == "autoJoin") {
|
||||||
@ -158,14 +192,16 @@ void Models::Room::update(const QString& field, const QVariant& value)
|
|||||||
setNick(value.toString());
|
setNick(value.toString());
|
||||||
} else if (field == "subject") {
|
} else if (field == "subject") {
|
||||||
setSubject(value.toString());
|
setSubject(value.toString());
|
||||||
} else {
|
} else if (field == "avatarState") {
|
||||||
Element::update(field, value);
|
setAvatarState(value.toUInt());
|
||||||
|
} else if (field == "avatarPath") {
|
||||||
|
setAvatarPath(value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon Models::Room::getStatusIcon(bool big) const
|
QIcon Models::Room::getStatusIcon(bool big) const
|
||||||
{
|
{
|
||||||
if (getMessagesCount() > 0) {
|
if (messages.size() > 0) {
|
||||||
return Shared::icon("mail-message", big);
|
return Shared::icon("mail-message", big);
|
||||||
} else {
|
} else {
|
||||||
if (autoJoin) {
|
if (autoJoin) {
|
||||||
@ -201,6 +237,42 @@ QString Models::Room::getStatusText() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int Models::Room::getMessagesCount() const
|
||||||
|
{
|
||||||
|
return messages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::addMessage(const Shared::Message& data)
|
||||||
|
{
|
||||||
|
messages.emplace_back(data);
|
||||||
|
changed(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
for (Shared::Message& msg : messages) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
msg.change(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::dropMessages()
|
||||||
|
{
|
||||||
|
if (messages.size() > 0) {
|
||||||
|
messages.clear();
|
||||||
|
changed(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::getMessages(Models::Room::Messages& container) const
|
||||||
|
{
|
||||||
|
for (Messages::const_iterator itr = messages.begin(), end = messages.end(); itr != end; ++itr) {
|
||||||
|
const Shared::Message& msg = *itr;
|
||||||
|
container.push_back(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Room::toOfflineState()
|
void Models::Room::toOfflineState()
|
||||||
{
|
{
|
||||||
@ -295,6 +367,47 @@ QString Models::Room::getDisplayedName() const
|
|||||||
return getRoomName();
|
return getRoomName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::Room::columnInvolvedInDisplay(int col)
|
||||||
|
{
|
||||||
|
return Item::columnInvolvedInDisplay(col) && col == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Models::Room::getAvatarPath() const
|
||||||
|
{
|
||||||
|
return avatarPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Avatar Models::Room::getAvatarState() const
|
||||||
|
{
|
||||||
|
return avatarState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::setAvatarPath(const QString& path)
|
||||||
|
{
|
||||||
|
if (avatarPath != path) {
|
||||||
|
avatarPath = path;
|
||||||
|
changed(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::setAvatarState(Shared::Avatar p_state)
|
||||||
|
{
|
||||||
|
if (avatarState != p_state) {
|
||||||
|
avatarState = p_state;
|
||||||
|
changed(7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Room::setAvatarState(unsigned int p_state)
|
||||||
|
{
|
||||||
|
if (p_state <= static_cast<quint8>(Shared::Avatar::valid)) {
|
||||||
|
Shared::Avatar state = static_cast<Shared::Avatar>(p_state);
|
||||||
|
setAvatarState(state);
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to set invalid avatar state" << p_state << "to the room" << jid << ", skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::map<QString, const Models::Participant &> Models::Room::getParticipants() const
|
std::map<QString, const Models::Participant &> Models::Room::getParticipants() const
|
||||||
{
|
{
|
||||||
std::map<QString, const Models::Participant&> result;
|
std::map<QString, const Models::Participant&> result;
|
||||||
@ -310,13 +423,8 @@ QString Models::Room::getParticipantIconPath(const QString& name) const
|
|||||||
{
|
{
|
||||||
std::map<QString, Models::Participant*>::const_iterator itr = participants.find(name);
|
std::map<QString, Models::Participant*>::const_iterator itr = participants.find(name);
|
||||||
if (itr == participants.end()) {
|
if (itr == participants.end()) {
|
||||||
std::map<QString, QString>::const_iterator eitr = exParticipantAvatars.find(name);
|
|
||||||
if (eitr != exParticipantAvatars.end()) {
|
|
||||||
return eitr->second;
|
|
||||||
} else {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return itr->second->getAvatarPath();
|
return itr->second->getAvatarPath();
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#ifndef MODELS_ROOM_H
|
#ifndef MODELS_ROOM_H
|
||||||
#define MODELS_ROOM_H
|
#define MODELS_ROOM_H
|
||||||
|
|
||||||
#include "element.h"
|
#include "item.h"
|
||||||
#include "participant.h"
|
#include "participant.h"
|
||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
@ -29,18 +29,21 @@ namespace Models {
|
|||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
*/
|
*/
|
||||||
class Room : public Element
|
class Room : public Models::Item
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Room(const Account* acc, const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
typedef std::deque<Shared::Message> Messages;
|
||||||
|
Room(const QString& p_jid, const QMap<QString, QVariant> &data, Item *parentItem = 0);
|
||||||
~Room();
|
~Room();
|
||||||
|
|
||||||
int columnCount() const override;
|
int columnCount() const override;
|
||||||
QVariant data(int column) const override;
|
QVariant data(int column) const override;
|
||||||
|
|
||||||
|
unsigned int getUnreadMessagesCount() const;
|
||||||
bool getJoined() const;
|
bool getJoined() const;
|
||||||
bool getAutoJoin() const;
|
bool getAutoJoin() const;
|
||||||
|
QString getJid() const;
|
||||||
QString getNick() const;
|
QString getNick() const;
|
||||||
QString getRoomName() const;
|
QString getRoomName() const;
|
||||||
QString getSubject() const;
|
QString getSubject() const;
|
||||||
@ -50,10 +53,17 @@ public:
|
|||||||
|
|
||||||
void setJoined(bool p_joined);
|
void setJoined(bool p_joined);
|
||||||
void setAutoJoin(bool p_autoJoin);
|
void setAutoJoin(bool p_autoJoin);
|
||||||
|
void setJid(const QString& p_jid);
|
||||||
void setNick(const QString& p_nick);
|
void setNick(const QString& p_nick);
|
||||||
void setSubject(const QString& sub);
|
void setSubject(const QString& sub);
|
||||||
|
|
||||||
void update(const QString& field, const QVariant& value) override;
|
void update(const QString& field, const QVariant& value);
|
||||||
|
|
||||||
|
void addMessage(const Shared::Message& data);
|
||||||
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
unsigned int getMessagesCount() const;
|
||||||
|
void dropMessages();
|
||||||
|
void getMessages(Messages& container) const;
|
||||||
|
|
||||||
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);
|
||||||
@ -61,6 +71,8 @@ public:
|
|||||||
|
|
||||||
void toOfflineState() override;
|
void toOfflineState() override;
|
||||||
QString getDisplayedName() const override;
|
QString getDisplayedName() const override;
|
||||||
|
Shared::Avatar getAvatarState() const;
|
||||||
|
QString getAvatarPath() const;
|
||||||
std::map<QString, const Participant&> getParticipants() const;
|
std::map<QString, const Participant&> getParticipants() const;
|
||||||
QString getParticipantIconPath(const QString& name) const;
|
QString getParticipantIconPath(const QString& name) const;
|
||||||
std::map<QString, QString> getExParticipantAvatars() const;
|
std::map<QString, QString> getExParticipantAvatars() const;
|
||||||
@ -72,14 +84,24 @@ signals:
|
|||||||
private:
|
private:
|
||||||
void handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data);
|
void handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool columnInvolvedInDisplay(int col) override;
|
||||||
|
void setAvatarState(Shared::Avatar p_state);
|
||||||
|
void setAvatarState(unsigned int p_state);
|
||||||
|
void setAvatarPath(const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool autoJoin;
|
bool autoJoin;
|
||||||
bool joined;
|
bool joined;
|
||||||
QString jid;
|
QString jid;
|
||||||
QString nick;
|
QString nick;
|
||||||
QString subject;
|
QString subject;
|
||||||
|
Shared::Avatar avatarState;
|
||||||
|
QString avatarPath;
|
||||||
|
Messages messages;
|
||||||
std::map<QString, Participant*> participants;
|
std::map<QString, Participant*> participants;
|
||||||
std::map<QString, QString> exParticipantAvatars;
|
std::map<QString, QString> exParticipantAvatars;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,11 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
break;
|
break;
|
||||||
case Item::presence: {
|
case Item::presence: {
|
||||||
Presence* contact = static_cast<Presence*>(item);
|
Presence* contact = static_cast<Presence*>(item);
|
||||||
QString str;
|
QString str("");
|
||||||
|
int mc = contact->getMessagesCount();
|
||||||
|
if (mc > 0) {
|
||||||
|
str += tr("New messages: ") + std::to_string(mc).c_str() + "\n";
|
||||||
|
}
|
||||||
Shared::Availability av = contact->getAvailability();
|
Shared::Availability av = contact->getAvailability();
|
||||||
str += tr("Availability: ") + Shared::Global::getName(av);
|
str += tr("Availability: ") + Shared::Global::getName(av);
|
||||||
QString s = contact->getStatus();
|
QString s = contact->getStatus();
|
||||||
@ -228,7 +232,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
break;
|
break;
|
||||||
case Item::participant: {
|
case Item::participant: {
|
||||||
Participant* p = static_cast<Participant*>(item);
|
Participant* p = static_cast<Participant*>(item);
|
||||||
QString str;
|
QString str("");
|
||||||
Shared::Availability av = p->getAvailability();
|
Shared::Availability av = p->getAvailability();
|
||||||
str += tr("Availability: ") + Shared::Global::getName(av) + "\n";
|
str += tr("Availability: ") + Shared::Global::getName(av) + "\n";
|
||||||
QString s = p->getStatus();
|
QString s = p->getStatus();
|
||||||
@ -256,7 +260,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
break;
|
break;
|
||||||
case Item::room: {
|
case Item::room: {
|
||||||
Room* rm = static_cast<Room*>(item);
|
Room* rm = static_cast<Room*>(item);
|
||||||
unsigned int count = rm->getMessagesCount();
|
unsigned int count = rm->getUnreadMessagesCount();
|
||||||
QString str("");
|
QString str("");
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
str += tr("New messages: ") + std::to_string(count).c_str() + "\n";
|
str += tr("New messages: ") + std::to_string(count).c_str() + "\n";
|
||||||
@ -446,10 +450,6 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
|||||||
std::map<ElId, Contact*>::iterator itr = contacts.find(id);
|
std::map<ElId, Contact*>::iterator itr = contacts.find(id);
|
||||||
if (itr == contacts.end()) {
|
if (itr == contacts.end()) {
|
||||||
contact = new Contact(acc, jid, data);
|
contact = new Contact(acc, jid, data);
|
||||||
connect(contact, &Contact::requestArchive, this, &Roster::onElementRequestArchive);
|
|
||||||
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
|
||||||
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
|
||||||
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
|
||||||
contacts.insert(std::make_pair(id, contact));
|
contacts.insert(std::make_pair(id, contact));
|
||||||
} else {
|
} else {
|
||||||
contact = itr->second;
|
contact = itr->second;
|
||||||
@ -535,19 +535,35 @@ 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({account, jid});
|
ElId id(account, jid);
|
||||||
if (el != NULL) {
|
std::map<ElId, Contact*>::iterator cItr = contacts.find(id);
|
||||||
|
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
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());
|
cItr->second->update(itr.key(), itr.value());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::iterator rItr = rooms.find(id);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
|
rItr->second->update(itr.key(), itr.value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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({account, jid});
|
ElId elid(account, jid);
|
||||||
if (el != NULL) {
|
std::map<ElId, Contact*>::iterator cItr = contacts.find(elid);
|
||||||
el->changeMessage(id, data);
|
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
|
cItr->second->changeMessage(id, data);
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::iterator rItr = rooms.find(elid);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
rItr->second->changeMessage(id, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,6 +627,7 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c
|
|||||||
} else {
|
} else {
|
||||||
delete ref;
|
delete ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gr->childCount() == 0) {
|
if (gr->childCount() == 0) {
|
||||||
removeGroup(account, group);
|
removeGroup(account, group);
|
||||||
}
|
}
|
||||||
@ -691,9 +708,29 @@ 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({account, data.getPenPalJid()});
|
ElId id(account, data.getPenPalJid());
|
||||||
if (el != NULL) {
|
std::map<ElId, Contact*>::iterator itr = contacts.find(id);
|
||||||
el->addMessage(data);
|
if (itr != contacts.end()) {
|
||||||
|
itr->second->addMessage(data);
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::const_iterator rItr = rooms.find(id);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
rItr->second->addMessage(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Roster::dropMessages(const QString& account, const QString& jid)
|
||||||
|
{
|
||||||
|
ElId id(account, jid);
|
||||||
|
std::map<ElId, Contact*>::iterator itr = contacts.find(id);
|
||||||
|
if (itr != contacts.end()) {
|
||||||
|
itr->second->dropMessages();
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::const_iterator rItr = rooms.find(id);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
rItr->second->dropMessages();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,11 +821,7 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Room* room = new Room(acc, jid, data);
|
Room* room = new Room(jid, data);
|
||||||
connect(room, &Contact::requestArchive, this, &Roster::onElementRequestArchive);
|
|
||||||
connect(room, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
|
||||||
connect(room, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
|
||||||
connect(room, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
|
||||||
rooms.insert(std::make_pair(id, room));
|
rooms.insert(std::make_pair(id, room));
|
||||||
acc->appendChild(room);
|
acc->appendChild(room);
|
||||||
}
|
}
|
||||||
@ -941,65 +974,3 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::Roster::onElementRequestArchive(const QString& before)
|
|
||||||
{
|
|
||||||
Element* el = static_cast<Element*>(sender());
|
|
||||||
emit requestArchive(el->getAccountName(), el->getJid(), before);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Roster::responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last)
|
|
||||||
{
|
|
||||||
ElId id(account, jid);
|
|
||||||
Element* el = getElement(id);
|
|
||||||
if (el != NULL) {
|
|
||||||
el->responseArchive(list, last);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
|
||||||
Element* el = getElement({info.account, info.jid});
|
|
||||||
if (el != NULL) {
|
|
||||||
el->fileProgress(info.messageId, value, up);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
|
||||||
Element* el = getElement({info.account, info.jid});
|
|
||||||
if (el != NULL) {
|
|
||||||
el->fileComplete(info.messageId, up);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
|
||||||
Element* el = getElement({info.account, info.jid});
|
|
||||||
if (el != NULL) {
|
|
||||||
el->fileError(info.messageId, err, up);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
|
||||||
{
|
|
||||||
std::map<ElId, Contact*>::iterator cItr = contacts.find(id);
|
|
||||||
|
|
||||||
if (cItr != contacts.end()) {
|
|
||||||
return cItr->second;
|
|
||||||
} else {
|
|
||||||
std::map<ElId, Room*>::iterator rItr = rooms.find(id);
|
|
||||||
if (rItr != rooms.end()) {
|
|
||||||
return rItr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
#include "shared/messageinfo.h"
|
|
||||||
#include "accounts.h"
|
#include "accounts.h"
|
||||||
#include "item.h"
|
#include "item.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
@ -59,6 +58,7 @@ public:
|
|||||||
void removePresence(const QString& account, const QString& jid, const QString& name);
|
void removePresence(const QString& account, const QString& jid, const QString& name);
|
||||||
void addMessage(const QString& account, const Shared::Message& data);
|
void addMessage(const QString& account, const Shared::Message& data);
|
||||||
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 dropMessages(const QString& account, const QString& jid);
|
||||||
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
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 changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void removeRoom(const QString& account, const QString jid);
|
void removeRoom(const QString& account, const QString jid);
|
||||||
@ -81,22 +81,15 @@ public:
|
|||||||
Account* getAccount(const QString& name);
|
Account* getAccount(const QString& name);
|
||||||
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);
|
||||||
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 fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up);
|
|
||||||
void fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up);
|
|
||||||
|
|
||||||
Accounts* accountsModel;
|
Accounts* accountsModel;
|
||||||
|
|
||||||
signals:
|
|
||||||
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
|
||||||
void fileDownloadRequest(const QString& url);
|
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
|
||||||
void localPathInvalid(const QString& path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Element* getElement(const ElId& id);
|
Item* root;
|
||||||
|
std::map<QString, Account*> accounts;
|
||||||
|
std::map<ElId, Group*> groups;
|
||||||
|
std::map<ElId, Contact*> contacts;
|
||||||
|
std::map<ElId, Room*> rooms;
|
||||||
|
|
||||||
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);
|
||||||
@ -107,14 +100,6 @@ private slots:
|
|||||||
void onChildRemoved();
|
void onChildRemoved();
|
||||||
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);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Item* root;
|
|
||||||
std::map<QString, Account*> accounts;
|
|
||||||
std::map<ElId, Group*> groups;
|
|
||||||
std::map<ElId, Contact*> contacts;
|
|
||||||
std::map<ElId, Room*> rooms;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class ElId {
|
class ElId {
|
||||||
|
242
ui/squawk.cpp
@ -29,6 +29,7 @@ Squawk::Squawk(QWidget *parent) :
|
|||||||
conversations(),
|
conversations(),
|
||||||
contextMenu(new QMenu()),
|
contextMenu(new QMenu()),
|
||||||
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
|
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
|
||||||
|
requestedFiles(),
|
||||||
vCards(),
|
vCards(),
|
||||||
requestedAccountsForPasswords(),
|
requestedAccountsForPasswords(),
|
||||||
prompt(0),
|
prompt(0),
|
||||||
@ -59,12 +60,8 @@ Squawk::Squawk(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);
|
||||||
//m_ui->mainToolBar->addWidget(m_ui->comboBox);
|
//m_ui->mainToolBar->addWidget(m_ui->comboBox);
|
||||||
|
|
||||||
@ -339,14 +336,17 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
|||||||
Models::Account* acc = rosterModel.getAccount(id->account);
|
Models::Account* acc = rosterModel.getAccount(id->account);
|
||||||
Conversation* conv = 0;
|
Conversation* conv = 0;
|
||||||
bool created = false;
|
bool created = false;
|
||||||
|
Models::Contact::Messages deque;
|
||||||
if (itr != conversations.end()) {
|
if (itr != conversations.end()) {
|
||||||
conv = itr->second;
|
conv = itr->second;
|
||||||
} else if (contact != 0) {
|
} else if (contact != 0) {
|
||||||
created = true;
|
created = true;
|
||||||
conv = new Chat(acc, contact);
|
conv = new Chat(acc, contact);
|
||||||
|
contact->getMessages(deque);
|
||||||
} else if (room != 0) {
|
} else if (room != 0) {
|
||||||
created = true;
|
created = true;
|
||||||
conv = new Room(acc, room);
|
conv = new Room(acc, room);
|
||||||
|
room->getMessages(deque);
|
||||||
|
|
||||||
if (!room->getJoined()) {
|
if (!room->getJoined()) {
|
||||||
emit setRoomJoined(id->account, id->name, true);
|
emit setRoomJoined(id->account, id->name, true);
|
||||||
@ -358,6 +358,12 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
|||||||
conv->setAttribute(Qt::WA_DeleteOnClose);
|
conv->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
subscribeConversation(conv);
|
subscribeConversation(conv);
|
||||||
conversations.insert(std::make_pair(*id, conv));
|
conversations.insert(std::make_pair(*id, conv));
|
||||||
|
|
||||||
|
if (created) {
|
||||||
|
for (Models::Contact::Messages::const_iterator itr = deque.begin(), end = deque.end(); itr != end; ++itr) {
|
||||||
|
conv->addMessage(*itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conv->show();
|
conv->show();
|
||||||
@ -374,6 +380,12 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Squawk::onConversationShown()
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
rosterModel.dropMessages(conv->getAccount(), conv->getJid());
|
||||||
|
}
|
||||||
|
|
||||||
void Squawk::onConversationClosed(QObject* parent)
|
void Squawk::onConversationClosed(QObject* parent)
|
||||||
{
|
{
|
||||||
Conversation* conv = static_cast<Conversation*>(sender());
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
@ -390,40 +402,164 @@ void Squawk::onConversationClosed(QObject* parent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up)
|
void Squawk::onConversationDownloadFile(const QString& messageId, const QString& url)
|
||||||
{
|
{
|
||||||
rosterModel.fileProgress(msgs, value, up);
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
std::map<QString, std::set<Models::Roster::ElId>>::iterator itr = requestedFiles.find(messageId);
|
||||||
|
bool created = false;
|
||||||
|
if (itr == requestedFiles.end()) {
|
||||||
|
itr = requestedFiles.insert(std::make_pair(messageId, std::set<Models::Roster::ElId>())).first;
|
||||||
|
created = true;
|
||||||
|
}
|
||||||
|
itr->second.insert(Models::Roster::ElId(conv->getAccount(), conv->getJid()));
|
||||||
|
if (created) {
|
||||||
|
emit downloadFileRequest(messageId, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path)
|
void Squawk::fileProgress(const QString& messageId, qreal value)
|
||||||
{
|
{
|
||||||
rosterModel.fileComplete(msgs, false);
|
std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId);
|
||||||
|
if (itr == requestedFiles.end()) {
|
||||||
|
qDebug() << "fileProgress in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const std::set<Models::Roster::ElId>& convs = itr->second;
|
||||||
|
for (std::set<Models::Roster::ElId>::const_iterator cItr = convs.begin(), cEnd = convs.end(); cItr != cEnd; ++cItr) {
|
||||||
|
const Models::Roster::ElId& id = *cItr;
|
||||||
|
Conversations::const_iterator c = conversations.find(id);
|
||||||
|
if (c != conversations.end()) {
|
||||||
|
c->second->responseFileProgress(messageId, value);
|
||||||
|
}
|
||||||
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
|
currentConversation->responseFileProgress(messageId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up)
|
void Squawk::fileError(const QString& messageId, const QString& error)
|
||||||
{
|
{
|
||||||
rosterModel.fileError(msgs, error, up);
|
std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId);
|
||||||
|
if (itr == requestedFiles.end()) {
|
||||||
|
qDebug() << "fileError in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const std::set<Models::Roster::ElId>& convs = itr->second;
|
||||||
|
for (std::set<Models::Roster::ElId>::const_iterator cItr = convs.begin(), cEnd = convs.end(); cItr != cEnd; ++cItr) {
|
||||||
|
const Models::Roster::ElId& id = *cItr;
|
||||||
|
Conversations::const_iterator c = conversations.find(id);
|
||||||
|
if (c != conversations.end()) {
|
||||||
|
c->second->fileError(messageId, error);
|
||||||
|
}
|
||||||
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
|
currentConversation->fileError(messageId, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestedFiles.erase(itr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path)
|
void Squawk::fileLocalPathResponse(const QString& messageId, const QString& path)
|
||||||
{
|
{
|
||||||
rosterModel.fileComplete(msgs, true);
|
std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId);
|
||||||
|
if (itr == requestedFiles.end()) {
|
||||||
|
qDebug() << "fileLocalPathResponse in UI Squawk but there is nobody waiting for that path, skipping";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const std::set<Models::Roster::ElId>& convs = itr->second;
|
||||||
|
for (std::set<Models::Roster::ElId>::const_iterator cItr = convs.begin(), cEnd = convs.end(); cItr != cEnd; ++cItr) {
|
||||||
|
const Models::Roster::ElId& id = *cItr;
|
||||||
|
Conversations::const_iterator c = conversations.find(id);
|
||||||
|
if (c != conversations.end()) {
|
||||||
|
c->second->responseLocalFile(messageId, path);
|
||||||
|
}
|
||||||
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
|
currentConversation->responseLocalFile(messageId, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedFiles.erase(itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::onConversationRequestLocalFile(const QString& messageId, const QString& url)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
std::map<QString, std::set<Models::Roster::ElId>>::iterator itr = requestedFiles.find(messageId);
|
||||||
|
bool created = false;
|
||||||
|
if (itr == requestedFiles.end()) {
|
||||||
|
itr = requestedFiles.insert(std::make_pair(messageId, std::set<Models::Roster::ElId>())).first;
|
||||||
|
created = true;
|
||||||
|
}
|
||||||
|
itr->second.insert(Models::Roster::ElId(conv->getAccount(), conv->getJid()));
|
||||||
|
if (created) {
|
||||||
|
emit fileLocalPathRequest(messageId, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::accountMessage(const QString& account, const Shared::Message& data)
|
void Squawk::accountMessage(const QString& account, const Shared::Message& data)
|
||||||
{
|
{
|
||||||
rosterModel.addMessage(account, data);
|
const QString& from = data.getPenPalJid();
|
||||||
}
|
Models::Roster::ElId id({account, from});
|
||||||
|
Conversations::iterator itr = conversations.find(id);
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
void Squawk::onUnnoticedMessage(const QString& account, const Shared::Message& msg)
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
{
|
currentConversation->addMessage(data);
|
||||||
notify(account, msg); //Telegram does this way - notifies even if the app is visible
|
|
||||||
QApplication::alert(this);
|
QApplication::alert(this);
|
||||||
|
if (!isVisible() && !data.getForwarded()) {
|
||||||
|
notify(account, data);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
Conversation* conv = itr->second;
|
||||||
|
conv->addMessage(data);
|
||||||
|
QApplication::alert(conv);
|
||||||
|
if (!found && conv->isMinimized()) {
|
||||||
|
rosterModel.addMessage(account, data);
|
||||||
|
}
|
||||||
|
if (!conv->isVisible() && !data.getForwarded()) {
|
||||||
|
notify(account, data);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
rosterModel.addMessage(account, data);
|
||||||
|
if (!data.getForwarded()) {
|
||||||
|
QApplication::alert(this);
|
||||||
|
notify(account, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
|
Models::Roster::ElId eid({account, jid});
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (currentConversation != 0 && currentConversation->getId() == eid) {
|
||||||
|
currentConversation->changeMessage(id, data);
|
||||||
|
QApplication::alert(this);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Conversations::iterator itr = conversations.find(eid);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
Conversation* conv = itr->second;
|
||||||
|
conv->changeMessage(id, data);
|
||||||
|
if (!found && conv->isMinimized()) {
|
||||||
rosterModel.changeMessage(account, jid, id, data);
|
rosterModel.changeMessage(account, jid, id, data);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
rosterModel.changeMessage(account, jid, id, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::notify(const QString& account, const Shared::Message& msg)
|
void Squawk::notify(const QString& account, const Shared::Message& msg)
|
||||||
@ -460,20 +596,60 @@ void Squawk::notify(const QString& account, const Shared::Message& msg)
|
|||||||
void Squawk::onConversationMessage(const Shared::Message& msg)
|
void Squawk::onConversationMessage(const Shared::Message& msg)
|
||||||
{
|
{
|
||||||
Conversation* conv = static_cast<Conversation*>(sender());
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
QString acc = conv->getAccount();
|
emit sendMessage(conv->getAccount(), msg);
|
||||||
|
Models::Roster::ElId id = conv->getId();
|
||||||
|
|
||||||
rosterModel.addMessage(acc, msg);
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
emit sendMessage(acc, msg);
|
if (conv == currentConversation) {
|
||||||
|
Conversations::iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
itr->second->addMessage(msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentConversation->addMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onRequestArchive(const QString& account, const QString& jid, const QString& before)
|
void Squawk::onConversationMessage(const Shared::Message& msg, const QString& path)
|
||||||
{
|
{
|
||||||
emit requestArchive(account, jid, 20, before); //TODO amount as a settings value
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
Models::Roster::ElId id = conv->getId();
|
||||||
|
std::map<QString, std::set<Models::Roster::ElId>>::iterator itr = requestedFiles.insert(std::make_pair(msg.getId(), std::set<Models::Roster::ElId>())).first;
|
||||||
|
itr->second.insert(id);
|
||||||
|
|
||||||
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
|
if (conv == currentConversation) {
|
||||||
|
Conversations::iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
itr->second->appendMessageWithUpload(msg, path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentConversation->appendMessageWithUpload(msg, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit sendMessage(conv->getAccount(), msg, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last)
|
void Squawk::onConversationRequestArchive(const QString& before)
|
||||||
{
|
{
|
||||||
rosterModel.responseArchive(account, jid, list, last);
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
requestArchive(conv->getAccount(), conv->getJid(), 20, before); //TODO amount as a settings value
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list)
|
||||||
|
{
|
||||||
|
Models::Roster::ElId id(account, jid);
|
||||||
|
|
||||||
|
if (currentConversation != 0 && currentConversation->getId() == id) {
|
||||||
|
currentConversation->responseArchive(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
itr->second->responseArchive(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::removeAccount(const QString& account)
|
void Squawk::removeAccount(const QString& account)
|
||||||
@ -485,6 +661,8 @@ void Squawk::removeAccount(const QString& account)
|
|||||||
++itr;
|
++itr;
|
||||||
Conversation* conv = lItr->second;
|
Conversation* conv = lItr->second;
|
||||||
disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||||
|
disconnect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive);
|
||||||
|
disconnect(conv, &Conversation::shown, this, &Squawk::onConversationShown);
|
||||||
conv->close();
|
conv->close();
|
||||||
conversations.erase(lItr);
|
conversations.erase(lItr);
|
||||||
} else {
|
} else {
|
||||||
@ -748,6 +926,7 @@ void Squawk::onActivateVCard(const QString& account, const QString& jid, bool ed
|
|||||||
{
|
{
|
||||||
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
|
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
|
||||||
VCard* card;
|
VCard* card;
|
||||||
|
Models::Contact::Messages deque;
|
||||||
if (itr != vCards.end()) {
|
if (itr != vCards.end()) {
|
||||||
card = itr->second;
|
card = itr->second;
|
||||||
} else {
|
} else {
|
||||||
@ -913,8 +1092,13 @@ void Squawk::onPasswordPromptRejected()
|
|||||||
void Squawk::subscribeConversation(Conversation* conv)
|
void Squawk::subscribeConversation(Conversation* conv)
|
||||||
{
|
{
|
||||||
connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
|
||||||
connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage);
|
connect(conv, qOverload<const Shared::Message&>(&Conversation::sendMessage), this, qOverload<const Shared::Message&>(&Squawk::onConversationMessage));
|
||||||
connect(conv, &Conversation::notifyableMessage, this, &Squawk::notify);
|
connect(conv, qOverload<const Shared::Message&, const QString&>(&Conversation::sendMessage),
|
||||||
|
this, qOverload<const Shared::Message&, const QString&>(&Squawk::onConversationMessage));
|
||||||
|
connect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive);
|
||||||
|
connect(conv, &Conversation::requestLocalFile, this, &Squawk::onConversationRequestLocalFile);
|
||||||
|
connect(conv, &Conversation::downloadFile, this, &Squawk::onConversationDownloadFile);
|
||||||
|
connect(conv, &Conversation::shown, this, &Squawk::onConversationShown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
|
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
|
||||||
@ -984,10 +1168,13 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
Models::Account* acc = rosterModel.getAccount(id->account);
|
Models::Account* acc = rosterModel.getAccount(id->account);
|
||||||
|
Models::Contact::Messages deque;
|
||||||
if (contact != 0) {
|
if (contact != 0) {
|
||||||
currentConversation = new Chat(acc, contact);
|
currentConversation = new Chat(acc, contact);
|
||||||
|
contact->getMessages(deque);
|
||||||
} else if (room != 0) {
|
} else if (room != 0) {
|
||||||
currentConversation = new Room(acc, room);
|
currentConversation = new Room(acc, room);
|
||||||
|
room->getMessages(deque);
|
||||||
|
|
||||||
if (!room->getJoined()) {
|
if (!room->getJoined()) {
|
||||||
emit setRoomJoined(id->account, id->name, true);
|
emit setRoomJoined(id->account, id->name, true);
|
||||||
@ -998,6 +1185,9 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
subscribeConversation(currentConversation);
|
subscribeConversation(currentConversation);
|
||||||
|
for (Models::Contact::Messages::const_iterator itr = deque.begin(), end = deque.end(); itr != end; ++itr) {
|
||||||
|
currentConversation->addMessage(*itr);
|
||||||
|
}
|
||||||
|
|
||||||
if (res.size() > 0) {
|
if (res.size() > 0) {
|
||||||
currentConversation->setPalResource(res);
|
currentConversation->setPalResource(res);
|
||||||
|
25
ui/squawk.h
@ -63,6 +63,7 @@ signals:
|
|||||||
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 sendMessage(const QString& account, const Shared::Message& data);
|
||||||
|
void sendMessage(const QString& account, const Shared::Message& data, const QString& path);
|
||||||
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);
|
||||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
@ -75,11 +76,11 @@ signals:
|
|||||||
void setRoomAutoJoin(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 fileLocalPathRequest(const QString& messageId, const QString& url);
|
||||||
|
void downloadFileRequest(const QString& messageId, 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 responsePassword(const QString& account, const QString& password);
|
void responsePassword(const QString& account, const QString& password);
|
||||||
void localPathInvalid(const QString& path);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void readSettings();
|
void readSettings();
|
||||||
@ -96,17 +97,16 @@ public slots:
|
|||||||
void removePresence(const QString& account, const QString& jid, const QString& name);
|
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 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 responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list);
|
||||||
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
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 changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void removeRoom(const QString& account, const QString jid);
|
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 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);
|
||||||
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
void fileError(const QString& messageId, const QString& error);
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileProgress(const QString& messageId, qreal value);
|
||||||
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);
|
void requestPassword(const QString& account);
|
||||||
@ -120,6 +120,7 @@ private:
|
|||||||
Conversations conversations;
|
Conversations conversations;
|
||||||
QMenu* contextMenu;
|
QMenu* contextMenu;
|
||||||
QDBusInterface dbus;
|
QDBusInterface dbus;
|
||||||
|
std::map<QString, std::set<Models::Roster::ElId>> requestedFiles;
|
||||||
std::map<QString, VCard*> vCards;
|
std::map<QString, VCard*> vCards;
|
||||||
std::deque<QString> requestedAccountsForPasswords;
|
std::deque<QString> requestedAccountsForPasswords;
|
||||||
QInputDialog* prompt;
|
QInputDialog* prompt;
|
||||||
@ -129,8 +130,6 @@ private:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent * event) override;
|
void closeEvent(QCloseEvent * event) override;
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void notify(const QString& account, const Shared::Message& msg);
|
void notify(const QString& account, const Shared::Message& msg);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@ -148,16 +147,18 @@ private slots:
|
|||||||
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 onConversationMessage(const Shared::Message& msg);
|
||||||
void onRequestArchive(const QString& account, const QString& jid, const QString& before);
|
void onConversationMessage(const Shared::Message& msg, const QString& path);
|
||||||
|
void onConversationRequestArchive(const QString& before);
|
||||||
void onRosterContextMenu(const QPoint& point);
|
void onRosterContextMenu(const QPoint& point);
|
||||||
|
void onConversationShown();
|
||||||
|
void onConversationRequestLocalFile(const QString& messageId, const QString& url);
|
||||||
|
void onConversationDownloadFile(const QString& messageId, const QString& url);
|
||||||
void onItemCollepsed(const QModelIndex& index);
|
void onItemCollepsed(const QModelIndex& index);
|
||||||
void onPasswordPromptAccepted();
|
void onPasswordPromptAccepted();
|
||||||
void onPasswordPromptRejected();
|
void onPasswordPromptRejected();
|
||||||
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||||
void onContextAboutToHide();
|
void onContextAboutToHide();
|
||||||
|
|
||||||
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void checkNextAccountForPassword();
|
void checkNextAccountForPassword();
|
||||||
void onPasswordPromptDone();
|
void onPasswordPromptDone();
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.3)
|
|
||||||
project(squawkUIUtils)
|
|
||||||
|
|
||||||
# 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 Core)
|
|
||||||
|
|
||||||
set(squawkUIUtils_SRC
|
|
||||||
# messageline.cpp
|
|
||||||
# message.cpp
|
|
||||||
resizer.cpp
|
|
||||||
# image.cpp
|
|
||||||
flowlayout.cpp
|
|
||||||
badge.cpp
|
|
||||||
progress.cpp
|
|
||||||
comboboxdelegate.cpp
|
|
||||||
feedview.cpp
|
|
||||||
messagedelegate.cpp
|
|
||||||
exponentialblur.cpp
|
|
||||||
shadowoverlay.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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)
|
|
@ -37,7 +37,7 @@ QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
|
|||||||
{
|
{
|
||||||
QComboBox *cb = new QComboBox(parent);
|
QComboBox *cb = new QComboBox(parent);
|
||||||
|
|
||||||
for (const std::pair<QString, QIcon>& pair : entries) {
|
for (const std::pair<QString, QIcon> pair : entries) {
|
||||||
cb->addItem(pair.second, pair.first);
|
cb->addItem(pair.second, pair.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "exponentialblur.h"
|
#include "dropshadoweffect.h"
|
||||||
|
#include "QtMath"
|
||||||
|
|
||||||
static const int tileSize = 32;
|
static const int tileSize = 32;
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -573,7 +574,128 @@ void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::exponentialblur(QImage& img, qreal radius, bool improvedQuality, int transposed)
|
PixmapFilter::PixmapFilter(QObject* parent):QObject(parent) {}
|
||||||
|
PixmapFilter::~PixmapFilter(){}
|
||||||
|
QRectF PixmapFilter::boundingRectFor(const QRectF &rect) const {return rect;}
|
||||||
|
|
||||||
|
PixmapDropShadowFilter::PixmapDropShadowFilter(QObject *parent):
|
||||||
|
PixmapFilter(parent),
|
||||||
|
mColor(63, 63, 63, 180),
|
||||||
|
mRadius(1),
|
||||||
|
mThickness(2),
|
||||||
|
top(true),
|
||||||
|
right(true),
|
||||||
|
bottom(true),
|
||||||
|
left(true){}
|
||||||
|
|
||||||
|
PixmapDropShadowFilter::~PixmapDropShadowFilter() {}
|
||||||
|
qreal PixmapDropShadowFilter::blurRadius() const {return mRadius;}
|
||||||
|
void PixmapDropShadowFilter::setBlurRadius(qreal radius) {mRadius = radius;}
|
||||||
|
QColor PixmapDropShadowFilter::color() const {return mColor;}
|
||||||
|
void PixmapDropShadowFilter::setColor(const QColor &color) {mColor = color;}
|
||||||
|
qreal PixmapDropShadowFilter::thickness() const {return mThickness;}
|
||||||
|
void PixmapDropShadowFilter::setThickness(qreal thickness) {mThickness = thickness;}
|
||||||
|
void PixmapDropShadowFilter::setFrame(bool ptop, bool pright, bool pbottom, bool pleft)
|
||||||
{
|
{
|
||||||
expblur<12, 10, false>(img, radius, improvedQuality, transposed);
|
top = ptop;
|
||||||
|
right = pright;
|
||||||
|
bottom = pbottom;
|
||||||
|
left = pleft;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DropShadowEffect::setThickness(qreal thickness)
|
||||||
|
{
|
||||||
|
if (filter.thickness() == thickness)
|
||||||
|
return;
|
||||||
|
|
||||||
|
filter.setThickness(thickness);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PixmapDropShadowFilter::draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src) const
|
||||||
|
{
|
||||||
|
if (px.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QImage tmp({px.width(), px.height() + int(mThickness)}, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
tmp.setDevicePixelRatio(px.devicePixelRatioF());
|
||||||
|
tmp.fill(0);
|
||||||
|
QPainter tmpPainter(&tmp);
|
||||||
|
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
|
if (top) {
|
||||||
|
QRectF shadow(0, 0, px.width(), mThickness);
|
||||||
|
tmpPainter.fillRect(shadow, mColor);
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
QRectF shadow(px.width() - mThickness, 0, mThickness, px.height());
|
||||||
|
tmpPainter.fillRect(shadow, mColor);
|
||||||
|
}
|
||||||
|
if (bottom) {
|
||||||
|
QRectF shadow(0, px.height() - mThickness, px.width(), mThickness * 2); //i have no idea why, but it leaves some unpainted stripe without some spare space
|
||||||
|
tmpPainter.fillRect(shadow, mColor);
|
||||||
|
}
|
||||||
|
if (left) {
|
||||||
|
QRectF shadow(0, 0, mThickness, px.height());
|
||||||
|
tmpPainter.fillRect(shadow, mColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
expblur<12, 10, false>(tmp, mRadius, false, 0);
|
||||||
|
tmpPainter.end();
|
||||||
|
|
||||||
|
// Draw the actual pixmap...
|
||||||
|
p->drawPixmap(pos, px, src);
|
||||||
|
|
||||||
|
// draw the blurred drop shadow...
|
||||||
|
p->drawImage(pos, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal DropShadowEffect::blurRadius() const {return filter.blurRadius();}
|
||||||
|
void DropShadowEffect::setBlurRadius(qreal blurRadius)
|
||||||
|
{
|
||||||
|
if (qFuzzyCompare(filter.blurRadius(), blurRadius))
|
||||||
|
return;
|
||||||
|
|
||||||
|
filter.setBlurRadius(blurRadius);
|
||||||
|
updateBoundingRect();
|
||||||
|
emit blurRadiusChanged(blurRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DropShadowEffect::setFrame(bool top, bool right, bool bottom, bool left)
|
||||||
|
{
|
||||||
|
filter.setFrame(top, right, bottom, left);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QColor DropShadowEffect::color() const {return filter.color();}
|
||||||
|
void DropShadowEffect::setColor(const QColor &color)
|
||||||
|
{
|
||||||
|
if (filter.color() == color)
|
||||||
|
return;
|
||||||
|
|
||||||
|
filter.setColor(color);
|
||||||
|
update();
|
||||||
|
emit colorChanged(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DropShadowEffect::draw(QPainter* painter)
|
||||||
|
{
|
||||||
|
if (filter.blurRadius() <= 0 && filter.thickness() == 0) {
|
||||||
|
drawSource(painter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PixmapPadMode mode = PadToEffectiveBoundingRect;
|
||||||
|
|
||||||
|
// Draw pixmap in device coordinates to avoid pixmap scaling.
|
||||||
|
QPoint offset;
|
||||||
|
const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);
|
||||||
|
if (pixmap.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTransform restoreTransform = painter->worldTransform();
|
||||||
|
painter->setWorldTransform(QTransform());
|
||||||
|
filter.draw(painter, offset, pixmap);
|
||||||
|
painter->setWorldTransform(restoreTransform);
|
||||||
}
|
}
|
93
ui/utils/dropshadoweffect.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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 DROPSHADOWEFFECT_H
|
||||||
|
#define DROPSHADOWEFFECT_H
|
||||||
|
|
||||||
|
#include <QGraphicsEffect>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
class PixmapFilter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
PixmapFilter(QObject *parent = nullptr);
|
||||||
|
virtual ~PixmapFilter() = 0;
|
||||||
|
|
||||||
|
virtual QRectF boundingRectFor(const QRectF &rect) const;
|
||||||
|
virtual void draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect = QRectF()) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PixmapDropShadowFilter : public PixmapFilter
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PixmapDropShadowFilter(QObject *parent = nullptr);
|
||||||
|
~PixmapDropShadowFilter();
|
||||||
|
|
||||||
|
void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const override;
|
||||||
|
|
||||||
|
qreal blurRadius() const;
|
||||||
|
void setBlurRadius(qreal radius);
|
||||||
|
|
||||||
|
QColor color() const;
|
||||||
|
void setColor(const QColor &color);
|
||||||
|
|
||||||
|
qreal thickness() const;
|
||||||
|
void setThickness(qreal thickness);
|
||||||
|
void setFrame(bool top, bool right, bool bottom, bool left);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QColor mColor;
|
||||||
|
qreal mRadius;
|
||||||
|
qreal mThickness;
|
||||||
|
bool top;
|
||||||
|
bool right;
|
||||||
|
bool bottom;
|
||||||
|
bool left;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DropShadowEffect : public QGraphicsEffect
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
qreal blurRadius() const;
|
||||||
|
QColor color() const;
|
||||||
|
void setFrame(bool top, bool right, bool bottom, bool left);
|
||||||
|
void setThickness(qreal thickness);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void blurRadiusChanged(qreal blurRadius);
|
||||||
|
void colorChanged(const QColor &color);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setBlurRadius(qreal blurRadius);
|
||||||
|
void setColor(const QColor &color);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void draw(QPainter * painter) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PixmapDropShadowFilter filter;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DROPSHADOWEFFECT_H
|
@ -1,34 +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 EXPONENTIALBLUR_H
|
|
||||||
#define EXPONENTIALBLUR_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QtMath>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Utils {
|
|
||||||
void exponentialblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // EXPONENTIALBLUR_H
|
|
@ -1,439 +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 "feedview.h"
|
|
||||||
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "messagedelegate.h"
|
|
||||||
#include "ui/models/messagefeed.h"
|
|
||||||
|
|
||||||
constexpr int maxMessageHeight = 10000;
|
|
||||||
constexpr int approximateSingleMessageHeight = 20;
|
|
||||||
constexpr int progressSize = 70;
|
|
||||||
|
|
||||||
const std::set<int> FeedView::geometryChangingRoles = {
|
|
||||||
Models::MessageFeed::Attach,
|
|
||||||
Models::MessageFeed::Text,
|
|
||||||
Models::MessageFeed::Id,
|
|
||||||
Models::MessageFeed::Error,
|
|
||||||
Models::MessageFeed::Date
|
|
||||||
};
|
|
||||||
|
|
||||||
FeedView::FeedView(QWidget* parent):
|
|
||||||
QAbstractItemView(parent),
|
|
||||||
hints(),
|
|
||||||
vo(0),
|
|
||||||
specialDelegate(false),
|
|
||||||
specialModel(false),
|
|
||||||
clearWidgetsMode(false),
|
|
||||||
modelState(Models::MessageFeed::complete),
|
|
||||||
progress()
|
|
||||||
{
|
|
||||||
horizontalScrollBar()->setRange(0, 0);
|
|
||||||
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
|
||||||
setMouseTracking(true);
|
|
||||||
setSelectionBehavior(SelectItems);
|
|
||||||
// viewport()->setAttribute(Qt::WA_Hover, true);
|
|
||||||
|
|
||||||
progress.setParent(viewport());
|
|
||||||
progress.resize(progressSize, progressSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
FeedView::~FeedView()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex FeedView::indexAt(const QPoint& point) const
|
|
||||||
{
|
|
||||||
int32_t vh = viewport()->height();
|
|
||||||
uint32_t y = vh - point.y() + vo;
|
|
||||||
|
|
||||||
for (std::deque<Hint>::size_type i = 0; i < hints.size(); ++i) {
|
|
||||||
const Hint& hint = hints[i];
|
|
||||||
if (y <= hint.offset + hint.height) {
|
|
||||||
if (y > hint.offset) {
|
|
||||||
return model()->index(i, 0, rootIndex());
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::scrollTo(const QModelIndex& index, QAbstractItemView::ScrollHint hint)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect FeedView::visualRect(const QModelIndex& index) const
|
|
||||||
{
|
|
||||||
unsigned int row = index.row();
|
|
||||||
if (!index.isValid() || row >= hints.size()) {
|
|
||||||
qDebug() << "visualRect for" << row;
|
|
||||||
return QRect();
|
|
||||||
} else {
|
|
||||||
const Hint& hint = hints.at(row);
|
|
||||||
const QWidget* vp = viewport();
|
|
||||||
return QRect(0, vp->height() - hint.height - hint.offset + vo, vp->width(), hint.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int FeedView::horizontalOffset() const
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FeedView::isIndexHidden(const QModelIndex& index) const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex FeedView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
|
|
||||||
{
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int FeedView::verticalOffset() const
|
|
||||||
{
|
|
||||||
return vo;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRegion FeedView::visualRegionForSelection(const QItemSelection& selection) const
|
|
||||||
{
|
|
||||||
return QRegion();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::rowsInserted(const QModelIndex& parent, int start, int end)
|
|
||||||
{
|
|
||||||
QAbstractItemView::rowsInserted(parent, start, end);
|
|
||||||
|
|
||||||
scheduleDelayedItemsLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
|
|
||||||
{
|
|
||||||
if (specialDelegate) {
|
|
||||||
for (int role : roles) {
|
|
||||||
if (geometryChangingRoles.count(role) != 0) {
|
|
||||||
scheduleDelayedItemsLayout(); //to recalculate layout only if there are some geometry changing modifications
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::updateGeometries()
|
|
||||||
{
|
|
||||||
qDebug() << "updateGeometries";
|
|
||||||
QScrollBar* bar = verticalScrollBar();
|
|
||||||
|
|
||||||
const QStyle* st = style();
|
|
||||||
const QAbstractItemModel* m = model();
|
|
||||||
QSize layoutBounds = maximumViewportSize();
|
|
||||||
QStyleOptionViewItem option = viewOptions();
|
|
||||||
option.rect.setHeight(maxMessageHeight);
|
|
||||||
option.rect.setWidth(layoutBounds.width());
|
|
||||||
int frameAroundContents = 0;
|
|
||||||
int verticalScrollBarExtent = st->pixelMetric(QStyle::PM_ScrollBarExtent, 0, bar);
|
|
||||||
|
|
||||||
bool layedOut = false;
|
|
||||||
if (verticalScrollBarExtent != 0 && verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded && m->rowCount() * approximateSingleMessageHeight < layoutBounds.height()) {
|
|
||||||
hints.clear();
|
|
||||||
layedOut = tryToCalculateGeometriesWithNoScrollbars(option, m, layoutBounds.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layedOut) {
|
|
||||||
bar->setRange(0, 0);
|
|
||||||
vo = 0;
|
|
||||||
} else {
|
|
||||||
int verticalMargin = 0;
|
|
||||||
if (st->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
|
|
||||||
frameAroundContents = st->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) {
|
|
||||||
verticalMargin = verticalScrollBarExtent + frameAroundContents;
|
|
||||||
}
|
|
||||||
|
|
||||||
layoutBounds.rwidth() -= verticalMargin;
|
|
||||||
|
|
||||||
option.features |= QStyleOptionViewItem::WrapText;
|
|
||||||
option.rect.setWidth(layoutBounds.width());
|
|
||||||
|
|
||||||
hints.clear();
|
|
||||||
uint32_t previousOffset = 0;
|
|
||||||
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
|
||||||
QModelIndex index = m->index(i, 0, rootIndex());
|
|
||||||
int height = itemDelegate(index)->sizeHint(option, index).height();
|
|
||||||
hints.emplace_back(Hint({
|
|
||||||
false,
|
|
||||||
previousOffset,
|
|
||||||
static_cast<uint32_t>(height)
|
|
||||||
}));
|
|
||||||
previousOffset += height;
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalHeight = previousOffset - layoutBounds.height();
|
|
||||||
if (modelState != Models::MessageFeed::complete) {
|
|
||||||
totalHeight += progressSize;
|
|
||||||
}
|
|
||||||
vo = qMax(qMin(vo, totalHeight), 0);
|
|
||||||
bar->setRange(0, totalHeight);
|
|
||||||
bar->setPageStep(layoutBounds.height());
|
|
||||||
bar->setValue(totalHeight - vo);
|
|
||||||
}
|
|
||||||
|
|
||||||
positionProgress();
|
|
||||||
|
|
||||||
if (specialDelegate) {
|
|
||||||
clearWidgetsMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QAbstractItemView::updateGeometries();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight)
|
|
||||||
{
|
|
||||||
uint32_t previousOffset = 0;
|
|
||||||
bool success = true;
|
|
||||||
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
|
||||||
QModelIndex index = m->index(i, 0, rootIndex());
|
|
||||||
int height = itemDelegate(index)->sizeHint(option, index).height();
|
|
||||||
|
|
||||||
if (previousOffset + height > totalHeight) {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
hints.emplace_back(Hint({
|
|
||||||
false,
|
|
||||||
previousOffset,
|
|
||||||
static_cast<uint32_t>(height)
|
|
||||||
}));
|
|
||||||
previousOffset += height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void FeedView::paintEvent(QPaintEvent* event)
|
|
||||||
{
|
|
||||||
//qDebug() << "paint" << event->rect();
|
|
||||||
const QAbstractItemModel* m = model();
|
|
||||||
QWidget* vp = viewport();
|
|
||||||
QRect zone = event->rect().translated(0, -vo);
|
|
||||||
uint32_t vph = vp->height();
|
|
||||||
int32_t y1 = zone.y();
|
|
||||||
int32_t y2 = y1 + zone.height();
|
|
||||||
|
|
||||||
bool inZone = false;
|
|
||||||
std::deque<QModelIndex> toRener;
|
|
||||||
for (std::deque<Hint>::size_type i = 0; i < hints.size(); ++i) {
|
|
||||||
const Hint& hint = hints[i];
|
|
||||||
int32_t relativeY1 = vph - hint.offset - hint.height;
|
|
||||||
if (!inZone) {
|
|
||||||
if (y2 > relativeY1) {
|
|
||||||
inZone = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inZone) {
|
|
||||||
toRener.emplace_back(m->index(i, 0, rootIndex()));
|
|
||||||
}
|
|
||||||
if (y1 > relativeY1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QPainter painter(vp);
|
|
||||||
QStyleOptionViewItem option = viewOptions();
|
|
||||||
option.features = QStyleOptionViewItem::WrapText;
|
|
||||||
QPoint cursor = vp->mapFromGlobal(QCursor::pos());
|
|
||||||
|
|
||||||
if (specialDelegate) {
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
if (clearWidgetsMode) {
|
|
||||||
del->beginClearWidgets();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QModelIndex& index : toRener) {
|
|
||||||
option.rect = visualRect(index);
|
|
||||||
bool mouseOver = option.rect.contains(cursor) && vp->rect().contains(cursor);
|
|
||||||
option.state.setFlag(QStyle::State_MouseOver, mouseOver);
|
|
||||||
itemDelegate(index)->paint(&painter, option, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clearWidgetsMode && specialDelegate) {
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
del->endClearWidgets();
|
|
||||||
clearWidgetsMode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->rect().height() == vp->height()) {
|
|
||||||
// draw the blurred drop shadow...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::verticalScrollbarValueChanged(int value)
|
|
||||||
{
|
|
||||||
vo = verticalScrollBar()->maximum() - value;
|
|
||||||
|
|
||||||
positionProgress();
|
|
||||||
|
|
||||||
if (specialDelegate) {
|
|
||||||
clearWidgetsMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modelState == Models::MessageFeed::incomplete && value < progressSize) {
|
|
||||||
model()->fetchMore(rootIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
if (!isVisible()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractItemView::mouseMoveEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::resizeEvent(QResizeEvent* event)
|
|
||||||
{
|
|
||||||
QAbstractItemView::resizeEvent(event);
|
|
||||||
|
|
||||||
positionProgress();
|
|
||||||
emit resized();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::positionProgress()
|
|
||||||
{
|
|
||||||
QSize layoutBounds = maximumViewportSize();
|
|
||||||
int progressPosition = layoutBounds.height() - progressSize;
|
|
||||||
std::deque<Hint>::size_type size = hints.size();
|
|
||||||
if (size > 0) {
|
|
||||||
const Hint& hint = hints[size - 1];
|
|
||||||
progressPosition -= hint.offset + hint.height;
|
|
||||||
}
|
|
||||||
progressPosition += vo;
|
|
||||||
progressPosition = qMin(progressPosition, 0);
|
|
||||||
|
|
||||||
progress.move((width() - progressSize) / 2, progressPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
QFont FeedView::getFont() const
|
|
||||||
{
|
|
||||||
return viewOptions().font;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
|
|
||||||
{
|
|
||||||
if (specialDelegate) {
|
|
||||||
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
|
||||||
disconnect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
|
||||||
disconnect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractItemView::setItemDelegate(delegate);
|
|
||||||
|
|
||||||
MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate);
|
|
||||||
if (del) {
|
|
||||||
specialDelegate = true;
|
|
||||||
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
|
||||||
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
|
||||||
} else {
|
|
||||||
specialDelegate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::setModel(QAbstractItemModel* p_model)
|
|
||||||
{
|
|
||||||
if (specialModel) {
|
|
||||||
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
|
||||||
disconnect(feed, &Models::MessageFeed::syncStateChange, this, &FeedView::onModelSyncStateChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractItemView::setModel(p_model);
|
|
||||||
|
|
||||||
Models::MessageFeed* feed = dynamic_cast<Models::MessageFeed*>(p_model);
|
|
||||||
if (feed) {
|
|
||||||
onModelSyncStateChange(feed->getSyncState());
|
|
||||||
specialModel = true;
|
|
||||||
connect(feed, &Models::MessageFeed::syncStateChange, this, &FeedView::onModelSyncStateChange);
|
|
||||||
} else {
|
|
||||||
onModelSyncStateChange(Models::MessageFeed::complete);
|
|
||||||
specialModel = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::onMessageButtonPushed(const QString& messageId, bool download)
|
|
||||||
{
|
|
||||||
if (specialModel) {
|
|
||||||
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
|
||||||
|
|
||||||
if (download) {
|
|
||||||
feed->downloadAttachment(messageId);
|
|
||||||
} else {
|
|
||||||
feed->uploadAttachment(messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::onMessageInvalidPath(const QString& messageId)
|
|
||||||
{
|
|
||||||
if (specialModel) {
|
|
||||||
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
|
||||||
feed->reportLocalPathInvalid(messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state)
|
|
||||||
{
|
|
||||||
bool needToUpdateGeometry = false;
|
|
||||||
if (modelState != state) {
|
|
||||||
if (state == Models::MessageFeed::complete || modelState == Models::MessageFeed::complete) {
|
|
||||||
needToUpdateGeometry = true;
|
|
||||||
}
|
|
||||||
modelState = state;
|
|
||||||
|
|
||||||
if (state == Models::MessageFeed::syncing) {
|
|
||||||
progress.show();
|
|
||||||
progress.start();
|
|
||||||
} else {
|
|
||||||
progress.stop();
|
|
||||||
progress.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needToUpdateGeometry) {
|
|
||||||
scheduleDelayedItemsLayout();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +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 FEEDVIEW_H
|
|
||||||
#define FEEDVIEW_H
|
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <ui/models/messagefeed.h>
|
|
||||||
#include "progress.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
class FeedView : public QAbstractItemView
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
FeedView(QWidget* parent = nullptr);
|
|
||||||
~FeedView();
|
|
||||||
|
|
||||||
QModelIndex indexAt(const QPoint & point) const override;
|
|
||||||
void scrollTo(const QModelIndex & index, QAbstractItemView::ScrollHint hint) override;
|
|
||||||
QRect visualRect(const QModelIndex & index) const override;
|
|
||||||
bool isIndexHidden(const QModelIndex & index) const override;
|
|
||||||
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
|
|
||||||
void setSelection(const QRect & rect, QItemSelectionModel::SelectionFlags command) override;
|
|
||||||
QRegion visualRegionForSelection(const QItemSelection & selection) const override;
|
|
||||||
void setItemDelegate(QAbstractItemDelegate* delegate);
|
|
||||||
void setModel(QAbstractItemModel * model) override;
|
|
||||||
|
|
||||||
QFont getFont() const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void resized();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void rowsInserted(const QModelIndex & parent, int start, int end) override;
|
|
||||||
void verticalScrollbarValueChanged(int value) override;
|
|
||||||
void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles) override;
|
|
||||||
void onMessageButtonPushed(const QString& messageId, bool download);
|
|
||||||
void onMessageInvalidPath(const QString& messageId);
|
|
||||||
void onModelSyncStateChange(Models::MessageFeed::SyncState state);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
int verticalOffset() const override;
|
|
||||||
int horizontalOffset() const override;
|
|
||||||
void paintEvent(QPaintEvent * event) override;
|
|
||||||
void updateGeometries() override;
|
|
||||||
void mouseMoveEvent(QMouseEvent * event) override;
|
|
||||||
void resizeEvent(QResizeEvent * event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
|
||||||
void positionProgress();
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Hint {
|
|
||||||
bool dirty;
|
|
||||||
uint32_t offset;
|
|
||||||
uint32_t height;
|
|
||||||
};
|
|
||||||
std::deque<Hint> hints;
|
|
||||||
int vo;
|
|
||||||
bool specialDelegate;
|
|
||||||
bool specialModel;
|
|
||||||
bool clearWidgetsMode;
|
|
||||||
Models::MessageFeed::SyncState modelState;
|
|
||||||
Progress progress;
|
|
||||||
|
|
||||||
static const std::set<int> geometryChangingRoles;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEEDVIEW_H
|
|
@ -1,549 +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 <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
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// }
|
|
@ -1,102 +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 MESSAGEDELEGATE_H
|
|
||||||
#define MESSAGEDELEGATE_H
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <QStyledItemDelegate>
|
|
||||||
#include <QStyleOptionButton>
|
|
||||||
#include <QFont>
|
|
||||||
#include <QFontMetrics>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QProgressBar>
|
|
||||||
#include <QLabel>
|
|
||||||
|
|
||||||
#include "shared/icons.h"
|
|
||||||
#include "shared/global.h"
|
|
||||||
#include "shared/utils.h"
|
|
||||||
|
|
||||||
namespace Models {
|
|
||||||
struct FeedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MessageDelegate : public QStyledItemDelegate
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
MessageDelegate(QObject *parent = nullptr);
|
|
||||||
~MessageDelegate();
|
|
||||||
|
|
||||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
|
||||||
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
|
||||||
//void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
|
|
||||||
|
|
||||||
void initializeFonts(const QFont& font);
|
|
||||||
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
|
|
||||||
void endClearWidgets();
|
|
||||||
void beginClearWidgets();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void buttonPushed(const QString& messageId, bool download) const;
|
|
||||||
void invalidPath(const QString& messageId) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
|
||||||
void paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
|
||||||
void paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
|
||||||
QPushButton* getButton(const Models::FeedItem& data) const;
|
|
||||||
QProgressBar* getBar(const Models::FeedItem& data) const;
|
|
||||||
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
|
||||||
QLabel* getBody(const Models::FeedItem& data) const;
|
|
||||||
void clearHelperWidget(const Models::FeedItem& data) const;
|
|
||||||
QSize calculateAttachSize(const QString& path, const QRect& bounds) const;
|
|
||||||
QSize constrainAttachSize(QSize src, QSize bounds) const;
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void onButtonPushed() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class FeedButton : public QPushButton {
|
|
||||||
public:
|
|
||||||
QString messageId;
|
|
||||||
bool download;
|
|
||||||
};
|
|
||||||
|
|
||||||
QFont bodyFont;
|
|
||||||
QFont nickFont;
|
|
||||||
QFont dateFont;
|
|
||||||
QFontMetrics bodyMetrics;
|
|
||||||
QFontMetrics nickMetrics;
|
|
||||||
QFontMetrics dateMetrics;
|
|
||||||
|
|
||||||
int buttonHeight;
|
|
||||||
int barHeight;
|
|
||||||
|
|
||||||
std::map<QString, FeedButton*>* buttons;
|
|
||||||
std::map<QString, QProgressBar*>* bars;
|
|
||||||
std::map<QString, QLabel*>* statusIcons;
|
|
||||||
std::map<QString, QLabel*>* bodies;
|
|
||||||
std::set<QString>* idsToKeep;
|
|
||||||
bool clearingWidgets;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MESSAGEDELEGATE_H
|
|
@ -1,91 +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 "shadowoverlay.h"
|
|
||||||
|
|
||||||
ShadowOverlay::ShadowOverlay(unsigned int r, unsigned int t, const QColor& c, QWidget* parent):
|
|
||||||
QWidget(parent),
|
|
||||||
top(false),
|
|
||||||
right(false),
|
|
||||||
bottom(false),
|
|
||||||
left(false),
|
|
||||||
thickness(t),
|
|
||||||
radius(r),
|
|
||||||
color(c),
|
|
||||||
shadow(1, 1, QImage::Format_ARGB32_Premultiplied)
|
|
||||||
{
|
|
||||||
setAttribute(Qt::WA_NoSystemBackground);
|
|
||||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowOverlay::paintEvent(QPaintEvent* event)
|
|
||||||
{
|
|
||||||
QWidget::paintEvent(event);
|
|
||||||
|
|
||||||
QPainter painter(this);
|
|
||||||
|
|
||||||
painter.drawImage(0, 0, shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowOverlay::resizeEvent(QResizeEvent* event)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(event);
|
|
||||||
|
|
||||||
updateImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowOverlay::updateImage()
|
|
||||||
{
|
|
||||||
int w = width();
|
|
||||||
int h = height();
|
|
||||||
shadow = QImage({w, h + int(thickness)}, QImage::Format_ARGB32_Premultiplied);
|
|
||||||
shadow.fill(0);
|
|
||||||
|
|
||||||
QPainter tmpPainter(&shadow);
|
|
||||||
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
|
|
||||||
if (top) {
|
|
||||||
QRectF shadow(0, 0, w, thickness);
|
|
||||||
tmpPainter.fillRect(shadow, color);
|
|
||||||
}
|
|
||||||
if (right) {
|
|
||||||
QRectF shadow(w - thickness, 0, thickness, h);
|
|
||||||
tmpPainter.fillRect(shadow, color);
|
|
||||||
}
|
|
||||||
if (bottom) {
|
|
||||||
QRectF shadow(0, h - thickness, w, thickness * 2); //i have no idea why, but it leaves some unpainted stripe without some spare space
|
|
||||||
tmpPainter.fillRect(shadow, color);
|
|
||||||
}
|
|
||||||
if (left) {
|
|
||||||
QRectF shadow(0, 0, thickness, h);
|
|
||||||
tmpPainter.fillRect(shadow, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils::exponentialblur(shadow, radius, false, 0);
|
|
||||||
tmpPainter.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShadowOverlay::setFrames(bool t, bool r, bool b, bool l)
|
|
||||||
{
|
|
||||||
top = t;
|
|
||||||
right = r;
|
|
||||||
bottom = b;
|
|
||||||
left = l;
|
|
||||||
|
|
||||||
updateImage();
|
|
||||||
update();
|
|
||||||
}
|
|
@ -1,58 +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 SHADOWOVERLAY_H
|
|
||||||
#define SHADOWOVERLAY_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QColor>
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QResizeEvent>
|
|
||||||
|
|
||||||
#include <ui/utils/exponentialblur.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
class ShadowOverlay : public QWidget {
|
|
||||||
|
|
||||||
public:
|
|
||||||
ShadowOverlay(unsigned int radius = 10, unsigned int thickness = 1, const QColor& color = Qt::black, QWidget* parent = nullptr);
|
|
||||||
|
|
||||||
void setFrames(bool top, bool right, bool bottom, bool left);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateImage();
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent * event) override;
|
|
||||||
void resizeEvent(QResizeEvent * event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool top;
|
|
||||||
bool right;
|
|
||||||
bool bottom;
|
|
||||||
bool left;
|
|
||||||
unsigned int thickness;
|
|
||||||
unsigned int radius;
|
|
||||||
QColor color;
|
|
||||||
QImage shadow;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SHADOWOVERLAY_H
|
|
@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
# Find the QtWidgets library
|
# Find the QtWidgets library
|
||||||
find_package(Qt5Widgets CONFIG REQUIRED COMPONENTS Widgets Core)
|
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||||
|
|
||||||
add_subdirectory(vcard)
|
add_subdirectory(vcard)
|
||||||
|
|
||||||
@ -21,11 +21,9 @@ set(squawkWidgets_SRC
|
|||||||
joinconference.cpp
|
joinconference.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(squawkWidgets STATIC ${squawkWidgets_SRC})
|
# Tell CMake to create the helloworld executable
|
||||||
|
add_library(squawkWidgets ${squawkWidgets_SRC})
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
# Use the Widgets module from Qt 5.
|
||||||
target_link_libraries(squawkWidgets vCardUI)
|
target_link_libraries(squawkWidgets vCardUI)
|
||||||
target_link_libraries(squawkWidgets squawkUIUtils)
|
|
||||||
target_link_libraries(squawkWidgets Qt5::Widgets)
|
target_link_libraries(squawkWidgets Qt5::Widgets)
|
||||||
|
|
||||||
qt5_use_modules(squawkWidgets Core Widgets)
|
|
||||||
|
@ -59,7 +59,7 @@ QMap<QString, QVariant> Account::value() const
|
|||||||
|
|
||||||
void Account::lockId()
|
void Account::lockId()
|
||||||
{
|
{
|
||||||
m_ui->name->setReadOnly(true);
|
m_ui->name->setReadOnly(true);;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::setData(const QMap<QString, QVariant>& data)
|
void Account::setData(const QMap<QString, QVariant>& data)
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
|
|
||||||
Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent):
|
Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent):
|
||||||
Conversation(false, acc, p_contact, p_contact->getJid(), "", parent),
|
Conversation(false, acc, p_contact->getJid(), "", parent),
|
||||||
contact(p_contact)
|
contact(p_contact)
|
||||||
{
|
{
|
||||||
setName(p_contact->getContactName());
|
setName(p_contact->getContactName());
|
||||||
@ -71,14 +71,31 @@ Shared::Message Chat::createMessage() const
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::onMessage(const Shared::Message& data)
|
void Chat::addMessage(const Shared::Message& data)
|
||||||
{
|
{
|
||||||
Conversation::onMessage(data);
|
Conversation::addMessage(data);
|
||||||
|
|
||||||
if (!data.getOutgoing()) {
|
if (!data.getOutgoing()) { //TODO need to check if that was the last message
|
||||||
const QString& res = data.getPenPalResource();
|
const QString& res = data.getPenPalResource();
|
||||||
if (res.size() > 0) {
|
if (res.size() > 0) {
|
||||||
setPalResource(res);
|
setPalResource(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Chat::setName(const QString& name)
|
||||||
|
{
|
||||||
|
Conversation::setName(name);
|
||||||
|
line->setPalName(getJid(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::setAvatar(const QString& path)
|
||||||
|
{
|
||||||
|
Conversation::setAvatar(path);
|
||||||
|
|
||||||
|
if (path.size() == 0) {
|
||||||
|
line->dropPalAvatar(contact->getJid());
|
||||||
|
} else {
|
||||||
|
line->setPalAvatar(contact->getJid(), path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,12 +35,15 @@ public:
|
|||||||
Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent = 0);
|
Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent = 0);
|
||||||
~Chat();
|
~Chat();
|
||||||
|
|
||||||
|
void addMessage(const Shared::Message & data) override;
|
||||||
|
void setAvatar(const QString& path) override;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onContactChanged(Models::Item* item, int row, int col);
|
void onContactChanged(Models::Item* item, int row, int col);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void setName(const QString & name) override;
|
||||||
Shared::Message createMessage() const override;
|
Shared::Message createMessage() const override;
|
||||||
void onMessage(const Shared::Message& msg) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateState();
|
void updateState();
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "conversation.h"
|
#include "conversation.h"
|
||||||
#include "ui_conversation.h"
|
#include "ui_conversation.h"
|
||||||
|
#include "ui/utils/dropshadoweffect.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
@ -28,44 +29,31 @@
|
|||||||
#include <QAbstractTextDocumentLayout>
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent):
|
Conversation::Conversation(bool muc, Models::Account* acc, const QString pJid, const QString pRes, QWidget* parent):
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
isMuc(muc),
|
isMuc(muc),
|
||||||
account(acc),
|
account(acc),
|
||||||
element(el),
|
|
||||||
palJid(pJid),
|
palJid(pJid),
|
||||||
activePalResource(pRes),
|
activePalResource(pRes),
|
||||||
|
line(new MessageLine(muc)),
|
||||||
m_ui(new Ui::Conversation()),
|
m_ui(new Ui::Conversation()),
|
||||||
ker(),
|
ker(),
|
||||||
|
scrollResizeCatcher(),
|
||||||
|
vis(),
|
||||||
thread(),
|
thread(),
|
||||||
statusIcon(0),
|
statusIcon(0),
|
||||||
statusLabel(0),
|
statusLabel(0),
|
||||||
filesLayout(0),
|
filesLayout(0),
|
||||||
overlay(new QWidget()),
|
overlay(new QWidget()),
|
||||||
filesToAttach(),
|
filesToAttach(),
|
||||||
feed(new FeedView()),
|
scroll(down),
|
||||||
delegate(new MessageDelegate(this)),
|
|
||||||
manualSliderChange(false),
|
manualSliderChange(false),
|
||||||
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1),
|
requestingHistory(false),
|
||||||
shadow(10, 1, Qt::black, this),
|
everShown(false),
|
||||||
contextMenu(new QMenu())
|
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
shadow.setFrames(true, false, true, false);
|
|
||||||
|
|
||||||
feed->setItemDelegate(delegate);
|
|
||||||
feed->setFrameShape(QFrame::NoFrame);
|
|
||||||
feed->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
delegate->initializeFonts(feed->getFont());
|
|
||||||
feed->setModel(el->feed);
|
|
||||||
el->feed->incrementObservers();
|
|
||||||
m_ui->widget->layout()->addWidget(feed);
|
|
||||||
|
|
||||||
connect(el->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage);
|
|
||||||
connect(feed, &FeedView::resized, this, &Conversation::positionShadow);
|
|
||||||
connect(feed, &FeedView::customContextMenuRequested, this, &Conversation::onFeedContext);
|
|
||||||
|
|
||||||
connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged);
|
connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged);
|
||||||
|
|
||||||
filesLayout = new FlowLayout(m_ui->filesPanel, 0);
|
filesLayout = new FlowLayout(m_ui->filesPanel, 0);
|
||||||
@ -75,7 +63,14 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
statusLabel = m_ui->statusLabel;
|
statusLabel = m_ui->statusLabel;
|
||||||
|
|
||||||
connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed);
|
connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed);
|
||||||
|
connect(&scrollResizeCatcher, &Resizer::resized, this, &Conversation::onScrollResize);
|
||||||
|
connect(&vis, &VisibilityCatcher::shown, this, &Conversation::onScrollResize);
|
||||||
|
connect(&vis, &VisibilityCatcher::hidden, this, &Conversation::onScrollResize);
|
||||||
connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed);
|
connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed);
|
||||||
|
connect(line, &MessageLine::resize, this, &Conversation::onMessagesResize);
|
||||||
|
connect(line, &MessageLine::downloadFile, this, &Conversation::downloadFile);
|
||||||
|
connect(line, &MessageLine::uploadFile, this, qOverload<const Shared::Message&, const QString&>(&Conversation::sendMessage));
|
||||||
|
connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile);
|
||||||
connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach);
|
connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach);
|
||||||
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
|
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
|
||||||
connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
|
connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
|
||||||
@ -83,43 +78,23 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
|
|
||||||
m_ui->messageEditor->installEventFilter(&ker);
|
m_ui->messageEditor->installEventFilter(&ker);
|
||||||
|
|
||||||
|
QScrollBar* vs = m_ui->scrollArea->verticalScrollBar();
|
||||||
|
m_ui->scrollArea->setWidget(line);
|
||||||
|
vs->installEventFilter(&vis);
|
||||||
|
|
||||||
//line->setAutoFillBackground(false);
|
line->setAutoFillBackground(false);
|
||||||
//if (testAttribute(Qt::WA_TranslucentBackground)) {
|
if (testAttribute(Qt::WA_TranslucentBackground)) {
|
||||||
//m_ui->scrollArea->setAutoFillBackground(false);
|
m_ui->scrollArea->setAutoFillBackground(false);
|
||||||
//} else {
|
} else {
|
||||||
//m_ui->scrollArea->setBackgroundRole(QPalette::Base);
|
m_ui->scrollArea->setBackgroundRole(QPalette::Base);
|
||||||
//}
|
|
||||||
|
|
||||||
//line->setMyAvatarPath(acc->getAvatarPath());
|
|
||||||
//line->setMyName(acc->getName());
|
|
||||||
|
|
||||||
initializeOverlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
Conversation::~Conversation()
|
|
||||||
{
|
|
||||||
delete contextMenu;
|
|
||||||
|
|
||||||
element->feed->decrementObservers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Conversation::onAccountChanged(Models::Item* item, int row, int col)
|
|
||||||
{
|
|
||||||
if (item == account) {
|
|
||||||
if (col == 2 && account->getState() == Shared::ConnectionState::connected) { //to request the history when we're back online after reconnect
|
|
||||||
//if (!requestingHistory) {
|
|
||||||
//requestingHistory = true;
|
|
||||||
//line->showBusyIndicator();
|
|
||||||
//emit requestArchive("");
|
|
||||||
//scroll = down;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Conversation::initializeOverlay()
|
connect(vs, &QScrollBar::valueChanged, this, &Conversation::onSliderValueChanged);
|
||||||
{
|
m_ui->scrollArea->installEventFilter(&scrollResizeCatcher);
|
||||||
|
|
||||||
|
line->setMyAvatarPath(acc->getAvatarPath());
|
||||||
|
line->setMyName(acc->getName());
|
||||||
|
|
||||||
QGridLayout* gr = static_cast<QGridLayout*>(layout());
|
QGridLayout* gr = static_cast<QGridLayout*>(layout());
|
||||||
QLabel* progressLabel = new QLabel(tr("Drop files here to attach them to your message"));
|
QLabel* progressLabel = new QLabel(tr("Drop files here to attach them to your message"));
|
||||||
gr->addWidget(overlay, 0, 0, 2, 1);
|
gr->addWidget(overlay, 0, 0, 2, 1);
|
||||||
@ -140,6 +115,36 @@ void Conversation::initializeOverlay()
|
|||||||
nl->addWidget(progressLabel);
|
nl->addWidget(progressLabel);
|
||||||
nl->addStretch();
|
nl->addStretch();
|
||||||
overlay->hide();
|
overlay->hide();
|
||||||
|
|
||||||
|
applyVisualEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
Conversation::~Conversation()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::onAccountChanged(Models::Item* item, int row, int col)
|
||||||
|
{
|
||||||
|
if (item == account) {
|
||||||
|
if (col == 2 && account->getState() == Shared::ConnectionState::connected) {
|
||||||
|
if (!requestingHistory) {
|
||||||
|
requestingHistory = true;
|
||||||
|
line->showBusyIndicator();
|
||||||
|
emit requestArchive("");
|
||||||
|
scroll = down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::applyVisualEffects()
|
||||||
|
{
|
||||||
|
DropShadowEffect *e1 = new DropShadowEffect;
|
||||||
|
e1->setBlurRadius(10);
|
||||||
|
e1->setColor(Qt::black);
|
||||||
|
e1->setThickness(1);
|
||||||
|
e1->setFrame(true, false, true, false);
|
||||||
|
m_ui->scrollArea->setGraphicsEffect(e1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::setName(const QString& name)
|
void Conversation::setName(const QString& name)
|
||||||
@ -158,6 +163,22 @@ QString Conversation::getJid() const
|
|||||||
return palJid;
|
return palJid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Conversation::addMessage(const Shared::Message& data)
|
||||||
|
{
|
||||||
|
int pos = m_ui->scrollArea->verticalScrollBar()->sliderPosition();
|
||||||
|
int max = m_ui->scrollArea->verticalScrollBar()->maximum();
|
||||||
|
|
||||||
|
MessageLine::Position place = line->message(data);
|
||||||
|
if (place == MessageLine::invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
line->changeMessage(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
KeyEnterReceiver::KeyEnterReceiver(QObject* parent): QObject(parent), ownEvent(false) {}
|
KeyEnterReceiver::KeyEnterReceiver(QObject* parent): QObject(parent), ownEvent(false) {}
|
||||||
|
|
||||||
bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event)
|
bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event)
|
||||||
@ -205,19 +226,93 @@ void Conversation::onEnterPressed()
|
|||||||
m_ui->messageEditor->clear();
|
m_ui->messageEditor->clear();
|
||||||
Shared::Message msg = createMessage();
|
Shared::Message msg = createMessage();
|
||||||
msg.setBody(body);
|
msg.setBody(body);
|
||||||
|
addMessage(msg);
|
||||||
emit sendMessage(msg);
|
emit sendMessage(msg);
|
||||||
}
|
}
|
||||||
if (filesToAttach.size() > 0) {
|
if (filesToAttach.size() > 0) {
|
||||||
for (Badge* badge : filesToAttach) {
|
for (Badge* badge : filesToAttach) {
|
||||||
Shared::Message msg = createMessage();
|
Shared::Message msg = createMessage();
|
||||||
msg.setAttachPath(badge->id);
|
line->appendMessageWithUpload(msg, badge->id);
|
||||||
element->feed->registerUpload(msg.getId());
|
usleep(1000); //this is required for the messages not to have equal time when appending into messageline
|
||||||
emit sendMessage(msg);
|
|
||||||
}
|
}
|
||||||
clearAttachedFiles();
|
clearAttachedFiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Conversation::appendMessageWithUpload(const Shared::Message& data, const QString& path)
|
||||||
|
{
|
||||||
|
line->appendMessageWithUploadNoSiganl(data, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::onMessagesResize(int amount)
|
||||||
|
{
|
||||||
|
manualSliderChange = true;
|
||||||
|
switch (scroll) {
|
||||||
|
case down:
|
||||||
|
m_ui->scrollArea->verticalScrollBar()->setValue(m_ui->scrollArea->verticalScrollBar()->maximum());
|
||||||
|
break;
|
||||||
|
case keep: {
|
||||||
|
int max = m_ui->scrollArea->verticalScrollBar()->maximum();
|
||||||
|
int value = m_ui->scrollArea->verticalScrollBar()->value() + amount;
|
||||||
|
m_ui->scrollArea->verticalScrollBar()->setValue(value);
|
||||||
|
|
||||||
|
if (value == max) {
|
||||||
|
scroll = down;
|
||||||
|
} else {
|
||||||
|
scroll = nothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
manualSliderChange = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::onSliderValueChanged(int value)
|
||||||
|
{
|
||||||
|
if (!manualSliderChange) {
|
||||||
|
if (value == m_ui->scrollArea->verticalScrollBar()->maximum()) {
|
||||||
|
scroll = down;
|
||||||
|
} else {
|
||||||
|
if (!requestingHistory && value == 0) {
|
||||||
|
requestingHistory = true;
|
||||||
|
line->showBusyIndicator();
|
||||||
|
emit requestArchive(line->firstMessageId());
|
||||||
|
scroll = keep;
|
||||||
|
} else {
|
||||||
|
scroll = nothing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::responseArchive(const std::list<Shared::Message> list)
|
||||||
|
{
|
||||||
|
requestingHistory = false;
|
||||||
|
scroll = keep;
|
||||||
|
|
||||||
|
line->hideBusyIndicator();
|
||||||
|
for (std::list<Shared::Message>::const_iterator itr = list.begin(), end = list.end(); itr != end; ++itr) {
|
||||||
|
addMessage(*itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::showEvent(QShowEvent* event)
|
||||||
|
{
|
||||||
|
if (!everShown) {
|
||||||
|
everShown = true;
|
||||||
|
line->showBusyIndicator();
|
||||||
|
requestingHistory = true;
|
||||||
|
scroll = keep;
|
||||||
|
emit requestArchive(line->firstMessageId());
|
||||||
|
}
|
||||||
|
emit shown();
|
||||||
|
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Conversation::onAttach()
|
void Conversation::onAttach()
|
||||||
{
|
{
|
||||||
QFileDialog* d = new QFileDialog(this, tr("Chose a file to send"));
|
QFileDialog* d = new QFileDialog(this, tr("Chose a file to send"));
|
||||||
@ -245,6 +340,34 @@ void Conversation::setStatus(const QString& status)
|
|||||||
statusLabel->setText(Shared::processMessageBody(status));
|
statusLabel->setText(Shared::processMessageBody(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Conversation::onScrollResize()
|
||||||
|
{
|
||||||
|
if (everShown) {
|
||||||
|
int size = m_ui->scrollArea->width();
|
||||||
|
QScrollBar* bar = m_ui->scrollArea->verticalScrollBar();
|
||||||
|
if (bar->isVisible() && !tsb) {
|
||||||
|
size -= bar->width();
|
||||||
|
|
||||||
|
}
|
||||||
|
line->setMaximumWidth(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::responseFileProgress(const QString& messageId, qreal progress)
|
||||||
|
{
|
||||||
|
line->fileProgress(messageId, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::fileError(const QString& messageId, const QString& error)
|
||||||
|
{
|
||||||
|
line->fileError(messageId, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Conversation::responseLocalFile(const QString& messageId, const QString& path)
|
||||||
|
{
|
||||||
|
line->responseLocalFile(messageId, path);
|
||||||
|
}
|
||||||
|
|
||||||
Models::Roster::ElId Conversation::getId() const
|
Models::Roster::ElId Conversation::getId() const
|
||||||
{
|
{
|
||||||
return {getAccount(), getJid()};
|
return {getAccount(), getJid()};
|
||||||
@ -321,7 +444,7 @@ void Conversation::onTextEditDocSizeChanged(const QSizeF& size)
|
|||||||
|
|
||||||
void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left)
|
void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left)
|
||||||
{
|
{
|
||||||
shadow.setFrames(top, right, bottom, left);
|
static_cast<DropShadowEffect*>(m_ui->scrollArea->graphicsEffect())->setFrame(top, right, bottom, left);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::dragEnterEvent(QDragEnterEvent* event)
|
void Conversation::dragEnterEvent(QDragEnterEvent* event)
|
||||||
@ -381,55 +504,21 @@ Shared::Message Conversation::createMessage() const
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::onFeedMessage(const Shared::Message& msg)
|
bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event)
|
||||||
{
|
{
|
||||||
this->onMessage(msg);
|
if (event->type() == QEvent::Show) {
|
||||||
|
emit shown();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type() == QEvent::Hide) {
|
||||||
|
emit hidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::onMessage(const Shared::Message& msg)
|
VisibilityCatcher::VisibilityCatcher(QWidget* parent):
|
||||||
|
QObject(parent)
|
||||||
{
|
{
|
||||||
if (!msg.getForwarded()) {
|
|
||||||
QApplication::alert(this);
|
|
||||||
if (window()->windowState().testFlag(Qt::WindowMinimized)) {
|
|
||||||
emit notifyableMessage(getAccount(), msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::positionShadow()
|
|
||||||
{
|
|
||||||
int w = width();
|
|
||||||
int h = feed->height();
|
|
||||||
|
|
||||||
shadow.resize(w, h);
|
|
||||||
shadow.move(feed->pos());
|
|
||||||
shadow.raise();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Conversation::onFeedContext(const QPoint& pos)
|
|
||||||
{
|
|
||||||
QModelIndex index = feed->indexAt(pos);
|
|
||||||
if (index.isValid()) {
|
|
||||||
Shared::Message* item = static_cast<Shared::Message*>(index.internalPointer());
|
|
||||||
|
|
||||||
contextMenu->clear();
|
|
||||||
bool showMenu = false;
|
|
||||||
QString path = item->getAttachPath();
|
|
||||||
if (path.size() > 0) {
|
|
||||||
showMenu = true;
|
|
||||||
QAction* open = contextMenu->addAction(Shared::icon("document-preview"), tr("Open"));
|
|
||||||
connect(open, &QAction::triggered, [path]() {
|
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
|
||||||
});
|
|
||||||
|
|
||||||
QAction* show = contextMenu->addAction(Shared::icon("folder"), tr("Show in folder"));
|
|
||||||
connect(show, &QAction::triggered, [path]() {
|
|
||||||
Shared::Global::highlightInFileManager(path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showMenu) {
|
|
||||||
contextMenu->popup(feed->viewport()->mapToGlobal(pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -24,20 +24,15 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QGraphicsOpacityEffect>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QAction>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
|
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "order.h"
|
#include "order.h"
|
||||||
#include "ui/models/account.h"
|
#include "ui/models/account.h"
|
||||||
#include "ui/models/roster.h"
|
#include "ui/models/roster.h"
|
||||||
|
#include "ui/utils/messageline.h"
|
||||||
|
#include "ui/utils/resizer.h"
|
||||||
#include "ui/utils/flowlayout.h"
|
#include "ui/utils/flowlayout.h"
|
||||||
#include "ui/utils/badge.h"
|
#include "ui/utils/badge.h"
|
||||||
#include "ui/utils/feedview.h"
|
|
||||||
#include "ui/utils/messagedelegate.h"
|
|
||||||
#include "ui/utils/shadowoverlay.h"
|
|
||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
#include "shared/utils.h"
|
#include "shared/utils.h"
|
||||||
|
|
||||||
@ -59,32 +54,54 @@ signals:
|
|||||||
void enterPressed();
|
void enterPressed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VisibilityCatcher : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VisibilityCatcher(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void hidden();
|
||||||
|
void shown();
|
||||||
|
};
|
||||||
|
|
||||||
class Conversation : public QWidget
|
class Conversation : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent = 0);
|
Conversation(bool muc, Models::Account* acc, const QString pJid, const QString pRes, QWidget* parent = 0);
|
||||||
~Conversation();
|
~Conversation();
|
||||||
|
|
||||||
QString getJid() const;
|
QString getJid() const;
|
||||||
QString getAccount() const;
|
QString getAccount() const;
|
||||||
QString getPalResource() const;
|
QString getPalResource() const;
|
||||||
Models::Roster::ElId getId() const;
|
Models::Roster::ElId getId() const;
|
||||||
|
virtual void addMessage(const Shared::Message& data);
|
||||||
|
|
||||||
void setPalResource(const QString& res);
|
void setPalResource(const QString& res);
|
||||||
|
void responseArchive(const std::list<Shared::Message> list);
|
||||||
|
void showEvent(QShowEvent * event) override;
|
||||||
|
void responseLocalFile(const QString& messageId, const QString& path);
|
||||||
|
void fileError(const QString& messageId, const QString& error);
|
||||||
|
void responseFileProgress(const QString& messageId, qreal progress);
|
||||||
virtual void setAvatar(const QString& path);
|
virtual void setAvatar(const QString& path);
|
||||||
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void setFeedFrames(bool top, bool right, bool bottom, bool left);
|
void setFeedFrames(bool top, bool right, bool bottom, bool left);
|
||||||
|
virtual void appendMessageWithUpload(const Shared::Message& data, const QString& path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendMessage(const Shared::Message& message);
|
void sendMessage(const Shared::Message& message);
|
||||||
|
void sendMessage(const Shared::Message& message, const QString& path);
|
||||||
void requestArchive(const QString& before);
|
void requestArchive(const QString& before);
|
||||||
void shown();
|
void shown();
|
||||||
void requestLocalFile(const QString& messageId, const QString& url);
|
void requestLocalFile(const QString& messageId, const QString& url);
|
||||||
void downloadFile(const QString& messageId, const QString& url);
|
void downloadFile(const QString& messageId, const QString& url);
|
||||||
void notifyableMessage(const QString& account, const Shared::Message& msg);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setName(const QString& name);
|
virtual void setName(const QString& name);
|
||||||
|
void applyVisualEffects();
|
||||||
virtual Shared::Message createMessage() const;
|
virtual Shared::Message createMessage() const;
|
||||||
void setStatus(const QString& status);
|
void setStatus(const QString& status);
|
||||||
void addAttachedFile(const QString& path);
|
void addAttachedFile(const QString& path);
|
||||||
@ -93,44 +110,47 @@ protected:
|
|||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
void initializeOverlay();
|
|
||||||
virtual void onMessage(const Shared::Message& msg);
|
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onEnterPressed();
|
void onEnterPressed();
|
||||||
|
void onMessagesResize(int amount);
|
||||||
|
void onSliderValueChanged(int value);
|
||||||
void onAttach();
|
void onAttach();
|
||||||
void onFileSelected();
|
void onFileSelected();
|
||||||
|
void onScrollResize();
|
||||||
void onBadgeClose();
|
void onBadgeClose();
|
||||||
void onClearButton();
|
void onClearButton();
|
||||||
void onTextEditDocSizeChanged(const QSizeF& size);
|
void onTextEditDocSizeChanged(const QSizeF& size);
|
||||||
void onAccountChanged(Models::Item* item, int row, int col);
|
void onAccountChanged(Models::Item* item, int row, int col);
|
||||||
void onFeedMessage(const Shared::Message& msg);
|
|
||||||
void positionShadow();
|
|
||||||
void onFeedContext(const QPoint &pos);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const bool isMuc;
|
const bool isMuc;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
enum Scroll {
|
||||||
|
nothing,
|
||||||
|
keep,
|
||||||
|
down
|
||||||
|
};
|
||||||
Models::Account* account;
|
Models::Account* account;
|
||||||
Models::Element* element;
|
|
||||||
QString palJid;
|
QString palJid;
|
||||||
QString activePalResource;
|
QString activePalResource;
|
||||||
|
MessageLine* line;
|
||||||
QScopedPointer<Ui::Conversation> m_ui;
|
QScopedPointer<Ui::Conversation> m_ui;
|
||||||
KeyEnterReceiver ker;
|
KeyEnterReceiver ker;
|
||||||
|
Resizer scrollResizeCatcher;
|
||||||
|
VisibilityCatcher vis;
|
||||||
QString thread;
|
QString thread;
|
||||||
QLabel* statusIcon;
|
QLabel* statusIcon;
|
||||||
QLabel* statusLabel;
|
QLabel* statusLabel;
|
||||||
FlowLayout* filesLayout;
|
FlowLayout* filesLayout;
|
||||||
QWidget* overlay;
|
QWidget* overlay;
|
||||||
W::Order<Badge*, Badge::Comparator> filesToAttach;
|
W::Order<Badge*, Badge::Comparator> filesToAttach;
|
||||||
FeedView* feed;
|
Scroll scroll;
|
||||||
MessageDelegate* delegate;
|
|
||||||
bool manualSliderChange;
|
bool manualSliderChange;
|
||||||
|
bool requestingHistory;
|
||||||
|
bool everShown;
|
||||||
bool tsb; //transient scroll bars
|
bool tsb; //transient scroll bars
|
||||||
|
|
||||||
ShadowOverlay shadow;
|
|
||||||
QMenu* contextMenu;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONVERSATION_H
|
#endif // CONVERSATION_H
|
||||||
|
@ -214,8 +214,62 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="midLineWidth">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QAbstractScrollArea::AdjustIgnored</enum>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>520</width>
|
||||||
|
<height>385</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
<zorder>scrollArea</zorder>
|
||||||
|
<zorder>widget_3</zorder>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QWidget" name="widget_2" native="true">
|
<widget class="QWidget" name="widget_2" native="true">
|
||||||
|
@ -19,16 +19,27 @@
|
|||||||
#include "room.h"
|
#include "room.h"
|
||||||
|
|
||||||
Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent):
|
Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent):
|
||||||
Conversation(true, acc, p_room, p_room->getJid(), "", parent),
|
Conversation(true, acc, p_room->getJid(), "", parent),
|
||||||
room(p_room)
|
room(p_room)
|
||||||
{
|
{
|
||||||
setName(p_room->getName());
|
setName(p_room->getName());
|
||||||
|
line->setMyName(room->getNick());
|
||||||
setStatus(room->getSubject());
|
setStatus(room->getSubject());
|
||||||
setAvatar(room->getAvatarPath());
|
setAvatar(room->getAvatarPath());
|
||||||
|
|
||||||
connect(room, &Models::Room::childChanged, this, &Room::onRoomChanged);
|
connect(room, &Models::Room::childChanged, this, &Room::onRoomChanged);
|
||||||
connect(room, &Models::Room::participantJoined, this, &Room::onParticipantJoined);
|
connect(room, &Models::Room::participantJoined, this, &Room::onParticipantJoined);
|
||||||
connect(room, &Models::Room::participantLeft, this, &Room::onParticipantLeft);
|
connect(room, &Models::Room::participantLeft, this, &Room::onParticipantLeft);
|
||||||
|
|
||||||
|
std::map<QString, const Models::Participant&> members = room->getParticipants();
|
||||||
|
for (std::pair<QString, const Models::Participant&> pair : members) {
|
||||||
|
QString aPath = pair.second.getAvatarPath();
|
||||||
|
if (aPath.size() > 0) {
|
||||||
|
line->setPalAvatar(pair.first, aPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line->setExPalAvatars(room->getExParticipantAvatars());
|
||||||
}
|
}
|
||||||
|
|
||||||
Room::~Room()
|
Room::~Room()
|
||||||
@ -64,14 +75,30 @@ void Room::onRoomChanged(Models::Item* item, int row, int col)
|
|||||||
setAvatar(room->getAvatarPath());
|
setAvatar(room->getAvatarPath());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
switch (col) {
|
||||||
|
case 7: {
|
||||||
|
Models::Participant* mem = static_cast<Models::Participant*>(item);
|
||||||
|
QString aPath = mem->getAvatarPath();
|
||||||
|
if (aPath.size() > 0) {
|
||||||
|
line->setPalAvatar(mem->getName(), aPath);
|
||||||
|
} else {
|
||||||
|
line->dropPalAvatar(mem->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::onParticipantJoined(const Models::Participant& participant)
|
void Room::onParticipantJoined(const Models::Participant& participant)
|
||||||
{
|
{
|
||||||
|
QString aPath = participant.getAvatarPath();
|
||||||
|
if (aPath.size() > 0) {
|
||||||
|
line->setPalAvatar(participant.getName(), aPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::onParticipantLeft(const QString& name)
|
void Room::onParticipantLeft(const QString& name)
|
||||||
{
|
{
|
||||||
|
line->movePalAvatarToEx(name);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ set(vCardUI_SRC
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
# Tell CMake to create the helloworld executable
|
||||||
add_library(vCardUI STATIC ${vCardUI_SRC})
|
add_library(vCardUI ${vCardUI_SRC})
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
# Use the Widgets module from Qt 5.
|
||||||
target_link_libraries(vCardUI Qt5::Widgets)
|
target_link_libraries(vCardUI Qt5::Widgets)
|
||||||
|