From c678a790e53d0125d2dba31bd760ca9c3ce0c8d1 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 14 Oct 2019 23:18:51 +0300 Subject: [PATCH 01/20] some more methods for archive to store avatar states --- core/CMakeLists.txt | 2 +- core/archive.cpp | 188 ++++++++++++++++++++++++++++++++------------ core/archive.h | 12 ++- core/rosteritem.cpp | 15 +++- core/rosteritem.h | 5 ++ main.cpp | 1 - 6 files changed, 168 insertions(+), 55 deletions(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 46bf97a..c541ba2 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -3,7 +3,7 @@ project(squawkCORE) set(CMAKE_AUTOMOC ON) -find_package(Qt5Widgets CONFIG REQUIRED) +find_package(Qt5Core CONFIG REQUIRED) find_package(Qt5Network CONFIG REQUIRED) set(squawkCORE_SRC diff --git a/core/archive.cpp b/core/archive.cpp index 00139ac..295b04c 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -32,7 +32,10 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent): environment(), main(), order(), - stats() + stats(), + hasAvatar(false), + avatarHash(), + avatarType() { } @@ -66,7 +69,26 @@ void Core::Archive::open(const QString& account) mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order); mdb_dbi_open(txn, "stats", MDB_CREATE, &stats); mdb_txn_commit(txn); - fromTheBeginning = _isFromTheBeginning(); + + mdb_txn_begin(environment, NULL, 0, &txn); + try { + fromTheBeginning = getStatBoolValue("beginning", txn); + } catch (NotFound e) { + fromTheBeginning = false; + } + try { + hasAvatar = getStatBoolValue("hasAvatar", txn); + } catch (NotFound e) { + hasAvatar = false; + } + if (hasAvatar) { + avatarHash = getStatStringValue("avatarHash", txn).c_str(); + avatarType = getStatStringValue("avatarType", txn).c_str(); + } else { + avatarHash = ""; + avatarType = ""; + } + mdb_txn_abort(txn); opened = true; } } @@ -396,40 +418,6 @@ std::list Core::Archive::getBefore(int count, const QString& id return res; } -bool Core::Archive::_isFromTheBeginning() -{ - std::string strKey = "beginning"; - - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = strKey.size(); - lmdbKey.mv_data = (char*)strKey.c_str(); - - MDB_txn *txn; - int rc; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); - if (rc == MDB_NOTFOUND) { - mdb_txn_abort(txn); - return false; - } else if (rc) { - qDebug() <<"isFromTheBeginning error: " << mdb_strerror(rc); - mdb_txn_abort(txn); - throw NotFound(strKey, jid.toStdString()); - } else { - uint8_t value = *(uint8_t*)(lmdbData.mv_data); - bool is; - if (value == 144) { - is = false; - } else if (value == 72) { - is = true; - } else { - qDebug() <<"isFromTheBeginning error: stored value doesn't match any magic number, the answer is most probably wrong"; - } - mdb_txn_abort(txn); - return is; - } -} - bool Core::Archive::isFromTheBeginning() { if (!opened) { @@ -445,26 +433,15 @@ void Core::Archive::setFromTheBeginning(bool is) } if (fromTheBeginning != is) { fromTheBeginning = is; - const std::string& id = "beginning"; - uint8_t value = 144; - if (is) { - value = 72; - } - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = sizeof value; - lmdbData.mv_data = &value; MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); - int rc; - rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); - if (rc != 0) { - qDebug() << "Couldn't store beginning key into stat database:" << mdb_strerror(rc); + bool success = setStatValue("beginning", is, txn); + if (success != 0) { mdb_txn_abort(txn); + } else { + mdb_txn_commit(txn); } - mdb_txn_commit(txn); } } @@ -508,3 +485,112 @@ void Core::Archive::printKeys() mdb_cursor_close(cursor); mdb_txn_abort(txn); } + +bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn) +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + + int rc; + rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); + if (rc == MDB_NOTFOUND) { + throw NotFound(id, jid.toStdString()); + } else if (rc) { + qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc); + throw 15; //TODO proper exception + } else { + uint8_t value = *(uint8_t*)(lmdbData.mv_data); + bool is; + if (value == 144) { + is = false; + } else if (value == 72) { + is = true; + } else { + qDebug() << "error retrieving boolean stat" << id.c_str() << ": stored value doesn't match any magic number, the answer is most probably wrong"; + throw NotFound(id, jid.toStdString()); + } + return is; + } +} + +std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn) +{ + MDB_cursor* cursor; + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + + int rc; + rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); + if (rc == MDB_NOTFOUND) { + throw NotFound(id, jid.toStdString()); + } else if (rc) { + qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc); + throw 15; //TODO proper exception + } else { + std::string value((char*)lmdbData.mv_data, lmdbData.mv_size); + return value; + } +} + +bool Core::Archive::setStatValue(const std::string& id, bool value, MDB_txn* txn) +{ + uint8_t binvalue = 144; + if (value) { + binvalue = 72; + } + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = sizeof binvalue; + lmdbData.mv_data = &binvalue; + int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc); + return false; + } + return true; +} + +bool Core::Archive::setStatValue(const std::string& id, const std::string& value, MDB_txn* txn) +{ + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = value.size(); + lmdbData.mv_data = (char*)value.c_str(); + int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc); + return false; + } + return true; +} + +bool Core::Archive::getHasAvatar() const +{ + if (!opened) { + throw Closed("getHasAvatar", jid.toStdString()); + } + + return hasAvatar; +} + +QString Core::Archive::getAvatarHash() const +{ + if (!opened) { + throw Closed("getAvatarHash", jid.toStdString()); + } + + return avatarHash; +} + +QString Core::Archive::getAvatarType() const +{ + if (!opened) { + throw Closed("getAvatarType", jid.toStdString()); + } + + return avatarType; +} diff --git a/core/archive.h b/core/archive.h index 58a5f8d..45a7599 100644 --- a/core/archive.h +++ b/core/archive.h @@ -49,6 +49,9 @@ public: std::list getBefore(int count, const QString& id); bool isFromTheBeginning(); void setFromTheBeginning(bool is); + bool getHasAvatar() const; + QString getAvatarHash() const; + QString getAvatarType() const; public: const QString jid; @@ -131,8 +134,15 @@ private: MDB_dbi main; MDB_dbi order; MDB_dbi stats; + bool hasAvatar; + QString avatarHash; + QString avatarType; - bool _isFromTheBeginning(); + bool getStatBoolValue(const std::string& id, MDB_txn* txn); + std::string getStatStringValue(const std::string& id, MDB_txn* txn); + + bool setStatValue(const std::string& id, bool value, MDB_txn* txn); + bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn); void printOrder(); void printKeys(); }; diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index be6ceee..50b3b83 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -20,9 +20,10 @@ #include -Core::RosterItem::RosterItem(const QString& pJid, const QString& account, QObject* parent): +Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObject* parent): QObject(parent), jid(pJid), + account(pAccount), name(), archiveState(empty), archive(new Archive(jid)), @@ -331,3 +332,15 @@ bool Core::RosterItem::isMuc() const { return muc; } + +QString Core::RosterItem::avatarHash() const +{ + return archive->getAvatarHash(); +} + +QString Core::RosterItem::avatarPath() const +{ + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + account + "/" + jid + "/avatar." + archive->getAvatarType(); + return path; +} diff --git a/core/rosteritem.h b/core/rosteritem.h index e4284af..a2bc929 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -58,6 +59,9 @@ public: void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId); void requestHistory(int count, const QString& before); void requestFromEmpty(int count, const QString& before); + bool hasAvatar() const; + QString avatarHash() const; + QString avatarPath() const; signals: void nameChanged(const QString& name); @@ -67,6 +71,7 @@ signals: public: const QString jid; + const QString account; protected: QString name; diff --git a/main.cpp b/main.cpp index 6fcf6c9..0b8c9a4 100644 --- a/main.cpp +++ b/main.cpp @@ -48,7 +48,6 @@ int main(int argc, char *argv[]) QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); bool found = false; for (QString share : shares) { - qDebug() << share; found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); if (found) { break; From 64e33b6139540af7688bfac30ec76f9c1088e9ac Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 15 Oct 2019 22:25:40 +0300 Subject: [PATCH 02/20] receiving avatars, generating missing avatars, storing state of avatars, global color palette --- core/CMakeLists.txt | 4 ++ core/account.cpp | 159 +++++++++++++++++++++++++++++++++----------- core/account.h | 6 ++ core/archive.cpp | 135 ++++++++++++++++++++++++++++++++++++- core/archive.h | 8 +++ core/rosteritem.cpp | 48 +++++++++++++ core/rosteritem.h | 7 ++ global.h | 46 +++++++++++++ 8 files changed, 375 insertions(+), 38 deletions(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index c541ba2..2c0374d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -4,7 +4,9 @@ project(squawkCORE) set(CMAKE_AUTOMOC ON) find_package(Qt5Core CONFIG REQUIRED) +find_package(Qt5Gui CONFIG REQUIRED) find_package(Qt5Network CONFIG REQUIRED) +find_package(Qt5Xml CONFIG REQUIRED) set(squawkCORE_SRC squawk.cpp @@ -30,5 +32,7 @@ endif() # Use the Widgets module from Qt 5. target_link_libraries(squawkCORE Qt5::Core) target_link_libraries(squawkCORE Qt5::Network) +target_link_libraries(squawkCORE Qt5::Gui) +target_link_libraries(squawkCORE Qt5::Xml) target_link_libraries(squawkCORE qxmpp) target_link_libraries(squawkCORE lmdb) diff --git a/core/account.cpp b/core/account.cpp index bacbb6d..bc7428c 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -48,37 +48,39 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); - QObject::connect(&client, SIGNAL(connected()), this, SLOT(onClientConnected())); - QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); - QObject::connect(&client, SIGNAL(presenceReceived(const QXmppPresence&)), this, SLOT(onPresenceReceived(const QXmppPresence&))); - QObject::connect(&client, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onMessageReceived(const QXmppMessage&))); - QObject::connect(&client, SIGNAL(error(QXmppClient::Error)), this, SLOT(onClientError(QXmppClient::Error))); + QObject::connect(&client, &QXmppClient::connected, this, &Account::onClientConnected); + QObject::connect(&client, &QXmppClient::disconnected, this, &Account::onClientDisconnected); + QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived); + QObject::connect(&client, &QXmppClient::messageReceived, this, &Account::onMessageReceived); + QObject::connect(&client, &QXmppClient::error, this, &Account::onClientError); QXmppRosterManager& rm = client.rosterManager(); - QObject::connect(&rm, SIGNAL(rosterReceived()), this, SLOT(onRosterReceived())); - QObject::connect(&rm, SIGNAL(itemAdded(const QString&)), this, SLOT(onRosterItemAdded(const QString&))); - QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&))); - QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&))); - //QObject::connect(&rm, SIGNAL(presenceChanged(const QString&, const QString&)), this, SLOT(onRosterPresenceChanged(const QString&, const QString&))); + QObject::connect(&rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived); + QObject::connect(&rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded); + QObject::connect(&rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved); + QObject::connect(&rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged); + //QObject::connect(&rm, &QXmppRosterManager::presenceChanged, this, &Account::onRosterPresenceChanged); client.addExtension(cm); - QObject::connect(cm, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onCarbonMessageReceived(const QXmppMessage&))); - QObject::connect(cm, SIGNAL(messageSent(const QXmppMessage&)), this, SLOT(onCarbonMessageSent(const QXmppMessage&))); + QObject::connect(cm, &QXmppCarbonManager::messageReceived, this, &Account::onCarbonMessageReceived); + QObject::connect(cm, &QXmppCarbonManager::messageSent, this, &Account::onCarbonMessageSent); client.addExtension(am); - QObject::connect(am, SIGNAL(logMessage(QXmppLogger::MessageType, const QString&)), this, SLOT(onMamLog(QXmppLogger::MessageType, const QString))); - QObject::connect(am, SIGNAL(archivedMessageReceived(const QString&, const QXmppMessage&)), this, SLOT(onMamMessageReceived(const QString&, const QXmppMessage&))); - QObject::connect(am, SIGNAL(resultsRecieved(const QString&, const QXmppResultSetReply&, bool)), - this, SLOT(onMamResultsReceived(const QString&, const QXmppResultSetReply&, bool))); + QObject::connect(am, &QXmppMamManager::logMessage, this, &Account::onMamLog); + QObject::connect(am, &QXmppMamManager::archivedMessageReceived, this, &Account::onMamMessageReceived); + QObject::connect(am, &QXmppMamManager::resultsRecieved, this, &Account::onMamResultsReceived); client.addExtension(mm); - QObject::connect(mm, SIGNAL(roomAdded(QXmppMucRoom*)), this, SLOT(onMucRoomAdded(QXmppMucRoom*))); + QObject::connect(mm, &QXmppMucManager::roomAdded, this, &Account::onMucRoomAdded); client.addExtension(bm); - QObject::connect(bm, SIGNAL(bookmarksReceived(const QXmppBookmarkSet&)), this, SLOT(bookmarksReceived(const QXmppBookmarkSet&))); + QObject::connect(bm, &QXmppBookmarkManager::bookmarksReceived, this, &Account::bookmarksReceived); + + QXmppVCardManager& vm = client.vCardManager(); + QObject::connect(&vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); } Account::~Account() @@ -279,6 +281,19 @@ void Core::Account::addedAccount(const QString& jid) {"name", re.name()}, {"state", state} }); + + if (contact->hasAvatar()) { + if (contact->isAvatarAutoGenerated()) { + cData.insert("avatarType", static_cast(Shared::Avatar::valid)); + } else { + cData.insert("avatarType", static_cast(Shared::Avatar::autocreated)); + } + cData.insert("avatarPath", contact->avatarPath()); + } else { + cData.insert("avatarType", static_cast(Shared::Avatar::empty)); + client.vCardManager().requestVCard(jid); + pendingVCardRequests.insert(jid); + } int grCount = 0; for (QSet::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) { const QString& groupName = *itr; @@ -296,36 +311,32 @@ void Core::Account::addedAccount(const QString& jid) void Core::Account::handleNewRosterItem(Core::RosterItem* contact) { - - QObject::connect(contact, SIGNAL(needHistory(const QString&, const QString&, const QDateTime&)), this, SLOT(onContactNeedHistory(const QString&, const QString&, const QDateTime&))); - QObject::connect(contact, SIGNAL(historyResponse(const std::list&)), this, SLOT(onContactHistoryResponse(const std::list&))); - QObject::connect(contact, SIGNAL(nameChanged(const QString&)), this, SLOT(onContactNameChanged(const QString&))); + QObject::connect(contact, &RosterItem::needHistory, this, &Account::onContactNeedHistory); + QObject::connect(contact, &RosterItem::historyResponse, this, &Account::onContactHistoryResponse); + QObject::connect(contact, &RosterItem::nameChanged, this, &Account::onContactNameChanged); + QObject::connect(contact, &RosterItem::avatarChanged, this, &Account::onContactAvatarChanged); } void Core::Account::handleNewContact(Core::Contact* contact) { handleNewRosterItem(contact); - QObject::connect(contact, SIGNAL(groupAdded(const QString&)), this, SLOT(onContactGroupAdded(const QString&))); - QObject::connect(contact, SIGNAL(groupRemoved(const QString&)), this, SLOT(onContactGroupRemoved(const QString&))); - QObject::connect(contact, SIGNAL(subscriptionStateChanged(Shared::SubscriptionState)), - this, SLOT(onContactSubscriptionStateChanged(Shared::SubscriptionState))); + QObject::connect(contact, &Contact::groupAdded, this, &Account::onContactGroupAdded); + QObject::connect(contact, &Contact::groupRemoved, this, &Account::onContactGroupRemoved); + QObject::connect(contact, &Contact::subscriptionStateChanged, this, &Account::onContactSubscriptionStateChanged); } void Core::Account::handleNewConference(Core::Conference* contact) { handleNewRosterItem(contact); - QObject::connect(contact, SIGNAL(nickChanged(const QString&)), this, SLOT(onMucNickNameChanged(const QString&))); - QObject::connect(contact, SIGNAL(subjectChanged(const QString&)), this, SLOT(onMucSubjectChanged(const QString&))); - QObject::connect(contact, SIGNAL(joinedChanged(bool)), this, SLOT(onMucJoinedChanged(bool))); - QObject::connect(contact, SIGNAL(autoJoinChanged(bool)), this, SLOT(onMucAutoJoinChanged(bool))); - QObject::connect(contact, SIGNAL(addParticipant(const QString&, const QMap&)), - this, SLOT(onMucAddParticipant(const QString&, const QMap&))); - QObject::connect(contact, SIGNAL(changeParticipant(const QString&, const QMap&)), - this, SLOT(onMucChangeParticipant(const QString&, const QMap&))); - QObject::connect(contact, SIGNAL(removeParticipant(const QString&)), this, SLOT(onMucRemoveParticipant(const QString&))); + QObject::connect(contact, &Conference::nickChanged, this, &Account::onMucNickNameChanged); + QObject::connect(contact, &Conference::subjectChanged, this, &Account::onMucSubjectChanged); + QObject::connect(contact, &Conference::joinedChanged, this, &Account::onMucJoinedChanged); + QObject::connect(contact, &Conference::autoJoinChanged, this, &Account::onMucAutoJoinChanged); + QObject::connect(contact, &Conference::addParticipant, this, &Account::onMucAddParticipant); + QObject::connect(contact, &Conference::changeParticipant, this, &Account::onMucChangeParticipant); + QObject::connect(contact, &Conference::removeParticipant, this, &Account::onMucRemoveParticipant); } - void Core::Account::onPresenceReceived(const QXmppPresence& presence) { QString id = presence.from(); @@ -341,11 +352,45 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence) } else { qDebug() << "Received a presence for another resource of my " << name << " account, skipping"; } + } else { + if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { + std::map::const_iterator itr = contacts.find(jid); + if (itr != contacts.end()) { + Contact* cnt = itr->second; + switch (presence.vCardUpdateType()) { + case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo + break; + case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here + break; + case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any + if (!cnt->hasAvatar() || (cnt->hasAvatar() && !cnt->isAvatarAutoGenerated())) { + cnt->setAutoGeneratedAvatar(); + } + break; + case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + if (cnt->hasAvatar()) { + if (cnt->isAvatarAutoGenerated()) { + client.vCardManager().requestVCard(jid); + pendingVCardRequests.insert(jid); + } else { + if (cnt->avatarHash() != presence.photoHash()) { + client.vCardManager().requestVCard(jid); + pendingVCardRequests.insert(jid); + } + } + } else { + client.vCardManager().requestVCard(jid); + pendingVCardRequests.insert(jid); + } + break; + } + } + } } switch (presence.type()) { case QXmppPresence::Error: - qDebug() << "An error reported by presence from " << id; + qDebug() << "An error reported by presence from" << id << presence.error().text(); break; case QXmppPresence::Available:{ QDateTime lastInteraction = presence.lastUserInteraction(); @@ -1211,3 +1256,43 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN rm.renameItem(jid, newName); } } + +void Core::Account::onVCardReceived(const QXmppVCardIq& card) +{ + QString jid = card.from(); + pendingVCardRequests.erase(jid); + RosterItem* item = 0; + + std::map::const_iterator contItr = contacts.find(jid); + if (contItr == contacts.end()) { + std::map::const_iterator confItr = conferences.find(jid); + if (confItr == conferences.end()) { + qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; + return; + } else { + item = confItr->second; + } + } else { + item = contItr->second; + } + + QByteArray ava = card.photo(); + if (ava.size() > 0) { + item->setAvatar(ava); + } else { + item->setAutoGeneratedAvatar(); + } +} + +void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& path) +{ + RosterItem* item = static_cast(sender()); + QMap cData({ + {"avatarType", static_cast(type)} + }); + if (type != Shared::Avatar::empty) { + cData.insert("avatarPath", path); + } + + emit changeContact(item->jid, cData); +} diff --git a/core/account.h b/core/account.h index 21f35d3..c1af059 100644 --- a/core/account.h +++ b/core/account.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "../global.h" #include "contact.h" #include "conference.h" @@ -119,6 +121,7 @@ private: std::map queuedContacts; std::set outOfRosterContacts; + std::set pendingVCardRequests; private slots: void onClientConnected(); @@ -157,8 +160,11 @@ private slots: void onContactSubscriptionStateChanged(Shared::SubscriptionState state); void onContactHistoryResponse(const std::list& list); void onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at); + void onContactAvatarChanged(Shared::Avatar, const QString& path); void onMamLog(QXmppLogger::MessageType type, const QString &msg); + + void onVCardReceived(const QXmppVCardIq& card); private: void addedAccount(const QString &bareJid); diff --git a/core/archive.cpp b/core/archive.cpp index 295b04c..5900df2 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -34,6 +34,7 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent): order(), stats(), hasAvatar(false), + avatarAutoGenerated(false), avatarHash(), avatarType() { @@ -82,13 +83,36 @@ void Core::Archive::open(const QString& account) hasAvatar = false; } if (hasAvatar) { - avatarHash = getStatStringValue("avatarHash", txn).c_str(); + try { + avatarAutoGenerated = getStatBoolValue("avatarAutoGenerated", txn); + } catch (NotFound e) { + avatarAutoGenerated = false; + } + avatarType = getStatStringValue("avatarType", txn).c_str(); + if (avatarAutoGenerated) { + avatarHash = ""; + } else { + avatarHash = getStatStringValue("avatarHash", txn).c_str(); + } } else { + avatarAutoGenerated = false; avatarHash = ""; avatarType = ""; } mdb_txn_abort(txn); + + if (hasAvatar) { + QFile ava(path + "/avatar." + avatarType); + if (!ava.exists()) { + bool success = dropAvatar(); + if (!success) { + qDebug() << "error opening archive" << jid << "for account" << account + << ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error"; + } + } + } + opened = true; } } @@ -577,6 +601,15 @@ bool Core::Archive::getHasAvatar() const return hasAvatar; } +bool Core::Archive::getAutoAvatar() const +{ + if (!opened) { + throw Closed("getAutoAvatar", jid.toStdString()); + } + + return avatarAutoGenerated; +} + QString Core::Archive::getAvatarHash() const { if (!opened) { @@ -594,3 +627,103 @@ QString Core::Archive::getAvatarType() const return avatarType; } + +bool Core::Archive::dropAvatar() +{ + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + bool success = setStatValue("hasAvatar", false, txn); + success = success && setStatValue("avatarAutoGenerated", false, txn); + success = success && setStatValue("avatarHash", "", txn); + success = success && setStatValue("avatarType", "", txn); + if (!success) { + mdb_txn_abort(txn); + return false; + } else { + hasAvatar = false; + avatarAutoGenerated = false; + avatarHash = ""; + avatarType = ""; + mdb_txn_commit(txn); + return true; + } +} + +bool Core::Archive::setAvatar(const QByteArray& data, bool generated) +{ + if (!opened) { + throw Closed("setAvatar", jid.toStdString()); + } + + if (data.size() == 0) { + if (!hasAvatar) { + return false; + } else { + return dropAvatar(); + } + } else { + const char* cep; + mdb_env_get_path(environment, &cep); + QString currentPath(cep); + bool needToRemoveOld = false; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(data); + QString newHash(hash.result()); + if (hasAvatar) { + if (!generated && !avatarAutoGenerated && avatarHash == newHash) { + return false; + } + QFile oldAvatar(currentPath + "/avatar." + avatarType); + if (oldAvatar.exists()) { + if (oldAvatar.rename(currentPath + "/avatar." + avatarType + ".bak")) { + needToRemoveOld = true; + } else { + qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName(); + return false; + } + } + } + QMimeDatabase db; + QMimeType type = db.mimeTypeForData(data); + QString ext = type.preferredSuffix(); + QFile newAvatar(currentPath + "/avatar." + ext); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(data); + newAvatar.close(); + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + bool success = setStatValue("hasAvatar", true, txn); + success = success && setStatValue("avatarAutoGenerated", generated, txn); + success = success && setStatValue("avatarHash", newHash.toStdString(), txn); + success = success && setStatValue("avatarType", ext.toStdString(), txn); + if (!success) { + qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state"; + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak"); + oldAvatar.rename(currentPath + "/avatar." + avatarType); + } + mdb_txn_abort(txn); + return false; + } else { + hasAvatar = true; + avatarAutoGenerated = generated; + avatarHash = newHash; + avatarType = ext; + mdb_txn_commit(txn); + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak"); + oldAvatar.remove(); + } + return true; + } + } else { + qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state"; + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak"); + oldAvatar.rename(currentPath + "/avatar." + avatarType); + } + return false; + } + } +} diff --git a/core/archive.h b/core/archive.h index 45a7599..e94fac8 100644 --- a/core/archive.h +++ b/core/archive.h @@ -20,6 +20,10 @@ #define CORE_ARCHIVE_H #include +#include +#include +#include + #include "../global.h" #include #include "../exception.h" @@ -50,8 +54,10 @@ public: bool isFromTheBeginning(); void setFromTheBeginning(bool is); bool getHasAvatar() const; + bool getAutoAvatar() const; QString getAvatarHash() const; QString getAvatarType() const; + bool setAvatar(const QByteArray& data, bool generated = false); public: const QString jid; @@ -135,6 +141,7 @@ private: MDB_dbi order; MDB_dbi stats; bool hasAvatar; + bool avatarAutoGenerated; QString avatarHash; QString avatarType; @@ -145,6 +152,7 @@ private: bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn); void printOrder(); void printKeys(); + bool dropAvatar(); }; } diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 50b3b83..f144901 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -338,9 +338,57 @@ QString Core::RosterItem::avatarHash() const return archive->getAvatarHash(); } +bool Core::RosterItem::isAvatarAutoGenerated() const +{ + return archive->getAutoAvatar(); +} + QString Core::RosterItem::avatarPath() const { QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + account + "/" + jid + "/avatar." + archive->getAvatarType(); return path; } + +bool Core::RosterItem::hasAvatar() const +{ + return archive->getHasAvatar(); +} + +void Core::RosterItem::setAvatar(const QByteArray& data) +{ + if (archive->setAvatar(data, false)) { + if (archive->getHasAvatar()) { + emit avatarChanged(Shared::Avatar::empty, ""); + } else { + emit avatarChanged(Shared::Avatar::valid, avatarPath()); + } + } +} + +void Core::RosterItem::setAutoGeneratedAvatar() +{ + QImage image(96, 96, QImage::Format_ARGB32_Premultiplied); + QPainter painter(&image); + quint8 colorIndex = rand() % Shared::colorPalette.size(); + const QColor& bg = Shared::colorPalette[colorIndex]; + painter.fillRect(image.rect(), bg); + QFont f; + f.setBold(true); + f.setPixelSize(72); + painter.setFont(f); + if (bg.lightnessF() > 0.5) { + painter.setPen(Qt::black); + } else { + painter.setPen(Qt::white); + } + painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, jid.at(0).toUpper()); + QByteArray arr; + QBuffer stream(&arr); + stream.open(QBuffer::WriteOnly); + image.save(&stream, "PNG"); + stream.close(); + if (archive->setAvatar(arr, true)) { + emit avatarChanged(Shared::Avatar::autocreated, avatarPath()); + } +} diff --git a/core/rosteritem.h b/core/rosteritem.h index a2bc929..c0a490e 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include @@ -60,14 +63,18 @@ public: void requestHistory(int count, const QString& before); void requestFromEmpty(int count, const QString& before); bool hasAvatar() const; + bool isAvatarAutoGenerated() const; QString avatarHash() const; QString avatarPath() const; + void setAvatar(const QByteArray& data); + void setAutoGeneratedAvatar(); signals: void nameChanged(const QString& name); void subscriptionStateChanged(Shared::SubscriptionState state); void historyResponse(const std::list& messages); void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()); + void avatarChanged(Shared::Avatar, const QString& path); public: const QString jid; diff --git a/global.h b/global.h index 77f89bf..ab84655 100644 --- a/global.h +++ b/global.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace Shared { @@ -69,6 +70,12 @@ enum class Role { moderator }; +enum class Avatar { + empty, + autocreated, + valid +}; + static const Availability availabilityHighest = offline; static const Availability availabilityLowest = online; @@ -102,6 +109,45 @@ static const std::deque affiliationNames = {"Unspecified", "Outcast", " static const std::deque roleNames = {"Unspecified", "Nobody", "Visitor", "Participant", "Moderator"}; QString generateUUID(); +static const std::vector colorPalette = { + QColor(244, 27, 63), + QColor(21, 104, 156), + QColor(38, 156, 98), + QColor(247, 103, 101), + QColor(121, 37, 117), + QColor(242, 202, 33), + QColor(168, 22, 63), + QColor(35, 100, 52), + QColor(52, 161, 152), + QColor(239, 53, 111), + QColor(237, 234, 36), + QColor(153, 148, 194), + QColor(211, 102, 151), + QColor(194, 63, 118), + QColor(249, 149, 51), + QColor(244, 206, 109), + QColor(121, 105, 153), + QColor(244, 199, 30), + QColor(28, 112, 28), + QColor(172, 18, 20), + QColor(25, 66, 110), + QColor(25, 149, 104), + QColor(214, 148, 0), + QColor(203, 47, 57), + QColor(4, 54, 84), + QColor(116, 161, 97), + QColor(50, 68, 52), + QColor(237, 179, 20), + QColor(69, 114, 147), + QColor(242, 212, 31), + QColor(248, 19, 20), + QColor(84, 102, 84), + QColor(25, 53, 122), + QColor(91, 91, 109), + QColor(17, 17, 80), + QColor(54, 54, 94) +}; + class Message { public: enum Type { From dc1ec1c9d4a0d7135298ecc8ad8015bd2853bf1c Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 16 Oct 2019 22:38:35 +0300 Subject: [PATCH 03/20] receiving account owner vCard, displaying avatars in roster --- CMakeLists.txt | 2 +- core/account.cpp | 202 +++++++++++++++++++++++++++++++++++++----- core/account.h | 15 +++- core/squawk.cpp | 66 +++++++------- core/squawk.h | 4 +- ui/models/account.cpp | 19 +++- ui/models/account.h | 4 + ui/models/contact.cpp | 60 ++++++++++++- ui/models/contact.h | 7 ++ ui/models/item.cpp | 2 +- ui/models/roster.cpp | 31 ++++++- ui/squawk.cpp | 4 + ui/squawk.ui | 6 +- 13 files changed, 358 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d32d2..5010adf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) project(squawk) set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) diff --git a/core/account.cpp b/core/account.cpp index bc7428c..a14e60f 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -41,7 +41,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& maxReconnectTimes(0), reconnectTimes(0), queuedContacts(), - outOfRosterContacts() + outOfRosterContacts(), + avatarHash(), + avatarType(), + ownVCardRequestInProgress(false) { config.setUser(p_login); config.setDomain(p_server); @@ -81,6 +84,52 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QXmppVCardManager& vm = client.vCardManager(); QObject::connect(&vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); + //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler + + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + name; + QDir dir(path); + + if (!dir.exists()) { + bool res = dir.mkpath(path); + if (!res) { + qDebug() << "Couldn't create a cache directory for account" << name; + throw 22; + } + } + + QFile* avatar = new QFile(path + "/avatar.png"); + QString type = "png"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpg"); + QString type = "jpg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpeg"); + QString type = "jpeg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.gif"); + QString type = "gif"; + } + } + } + + if (avatar->exists()) { + if (avatar->open(QFile::ReadOnly)) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(avatar); + avatarHash = sha1.result(); + avatarType = type; + } + } + if (avatarType.size() != 0) { + presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + presence.setPhotoHash(avatarHash.toUtf8()); + } else { + presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); + } } Account::~Account() @@ -191,6 +240,9 @@ QString Core::Account::getServer() const void Core::Account::onRosterReceived() { + client.vCardManager().requestClientVCard(); //TODO need to make sure server actually supports vCards + ownVCardRequestInProgress = true; + QXmppRosterManager& rm = client.rosterManager(); QStringList bj = rm.getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { @@ -283,14 +335,15 @@ void Core::Account::addedAccount(const QString& jid) }); if (contact->hasAvatar()) { - if (contact->isAvatarAutoGenerated()) { - cData.insert("avatarType", static_cast(Shared::Avatar::valid)); + if (!contact->isAvatarAutoGenerated()) { + cData.insert("avatarState", static_cast(Shared::Avatar::valid)); } else { - cData.insert("avatarType", static_cast(Shared::Avatar::autocreated)); + cData.insert("avatarState", static_cast(Shared::Avatar::autocreated)); } cData.insert("avatarPath", contact->avatarPath()); } else { - cData.insert("avatarType", static_cast(Shared::Avatar::empty)); + cData.insert("avatarState", static_cast(Shared::Avatar::empty)); + cData.insert("avatarPath", ""); client.vCardManager().requestVCard(jid); pendingVCardRequests.insert(jid); } @@ -337,9 +390,9 @@ void Core::Account::handleNewConference(Core::Conference* contact) QObject::connect(contact, &Conference::removeParticipant, this, &Account::onMucRemoveParticipant); } -void Core::Account::onPresenceReceived(const QXmppPresence& presence) +void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { - QString id = presence.from(); + QString id = p_presence.from(); QStringList comps = id.split("/"); QString jid = comps.front(); QString resource = comps.back(); @@ -348,16 +401,35 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence) if (jid == myJid) { if (resource == getResource()) { - emit availabilityChanged(presence.availableStatusType()); + emit availabilityChanged(p_presence.availableStatusType()); } else { - qDebug() << "Received a presence for another resource of my " << name << " account, skipping"; + if (!ownVCardRequestInProgress) { + switch (p_presence.vCardUpdateType()) { + case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo + break; + case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here + break; + case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any + if (avatarType.size() > 0) { + client.vCardManager().requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + if (avatarHash != p_presence.photoHash()) { + client.vCardManager().requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + } + } } } else { if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { std::map::const_iterator itr = contacts.find(jid); if (itr != contacts.end()) { Contact* cnt = itr->second; - switch (presence.vCardUpdateType()) { + switch (p_presence.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here @@ -373,7 +445,7 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence) client.vCardManager().requestVCard(jid); pendingVCardRequests.insert(jid); } else { - if (cnt->avatarHash() != presence.photoHash()) { + if (cnt->avatarHash() != p_presence.photoHash()) { client.vCardManager().requestVCard(jid); pendingVCardRequests.insert(jid); } @@ -388,19 +460,19 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence) } } - switch (presence.type()) { + switch (p_presence.type()) { case QXmppPresence::Error: - qDebug() << "An error reported by presence from" << id << presence.error().text(); + qDebug() << "An error reported by presence from" << id << p_presence.error().text(); break; case QXmppPresence::Available:{ - QDateTime lastInteraction = presence.lastUserInteraction(); + QDateTime lastInteraction = p_presence.lastUserInteraction(); if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTime(); } emit addPresence(jid, resource, { {"lastActivity", lastInteraction}, - {"availability", presence.availableStatusType()}, //TODO check and handle invisible - {"status", presence.statusText()} + {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible + {"status", p_presence.statusText()} }); } break; @@ -1267,7 +1339,11 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) if (contItr == contacts.end()) { std::map::const_iterator confItr = conferences.find(jid); if (confItr == conferences.end()) { - qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; + if (jid == getLogin() + "@" + getServer()) { + onOwnVCardReceived(card); + } else { + qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; + } return; } else { item = confItr->second; @@ -1284,15 +1360,99 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) } } +void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) +{ + QByteArray ava = card.photo(); + bool changed = false; + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/"; + if (ava.size() > 0) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(ava); + QString newHash(sha1.result()); + QMimeDatabase db; + QMimeType newType = db.mimeTypeForData(ava); + if (avatarType.size() > 0) { + if (avatarHash != newHash) { + QString oldPath = path + "avatar." + avatarType; + QFile oldAvatar(oldPath); + bool oldToRemove = false; + if (oldAvatar.exists()) { + if (oldAvatar.rename(oldPath + ".bak")) { + oldToRemove = true; + } else { + qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing"; + } + } + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + changed = true; + } else { + qDebug() << "Received new avatar for account" << name << "but can't save it"; + if (oldToRemove) { + qDebug() << "rolling back to the old avatar"; + if (!oldAvatar.rename(oldPath)) { + qDebug() << "Couldn't roll back to the old avatar in account" << name; + } + } + } + } + } else { + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + changed = true; + } else { + qDebug() << "Received new avatar for account" << name << "but can't save it"; + } + } + } else { + if (avatarType.size() > 0) { + QFile oldAvatar(path + "avatar." + avatarType); + if (!oldAvatar.remove()) { + qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing"; + } else { + changed = true; + } + } + } + + if (changed) { + QMap change; + if (avatarType.size() > 0) { + presence.setPhotoHash(avatarHash.toUtf8()); + presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + change.insert("avatarPath", path + "avatar." + avatarType); + } else { + presence.setPhotoHash(""); + presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); + change.insert("avatarPath", ""); + } + client.setClientPresence(presence); + } + + ownVCardRequestInProgress = false; +} + +QString Core::Account::getAvatarPath() const +{ + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType; +} + void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& path) { RosterItem* item = static_cast(sender()); QMap cData({ - {"avatarType", static_cast(type)} + {"avatarState", static_cast(type)}, + {"avatarPath", path} }); - if (type != Shared::Avatar::empty) { - cData.insert("avatarPath", path); - } emit changeContact(item->jid, cData); } + diff --git a/core/account.h b/core/account.h index c1af059..f9d8469 100644 --- a/core/account.h +++ b/core/account.h @@ -19,7 +19,13 @@ #ifndef CORE_ACCOUNT_H #define CORE_ACCOUNT_H -#include +#include +#include +#include +#include +#include +#include + #include #include @@ -56,6 +62,7 @@ public: QString getServer() const; QString getPassword() const; QString getResource() const; + QString getAvatarPath() const; Shared::Availability getAvailability() const; void setName(const QString& p_name); @@ -82,6 +89,7 @@ public: void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); signals: + void changed(const QMap& data); void connectionStateChanged(int); void availabilityChanged(int); void addGroup(const QString& name); @@ -123,6 +131,10 @@ private: std::set outOfRosterContacts; std::set pendingVCardRequests; + QString avatarHash; + QString avatarType; + bool ownVCardRequestInProgress; + private slots: void onClientConnected(); void onClientDisconnected(); @@ -165,6 +177,7 @@ private slots: void onMamLog(QXmppLogger::MessageType type, const QString &msg); void onVCardReceived(const QXmppVCardIq& card); + void onOwnVCardReceived(const QXmppVCardIq& card); private: void addedAccount(const QString &bareJid); diff --git a/core/squawk.cpp b/core/squawk.cpp index e624a8b..61b624d 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -28,9 +28,9 @@ Core::Squawk::Squawk(QObject* parent): amap(), network() { - connect(&network, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), this, SIGNAL(fileLocalPathResponse(const QString&, const QString&))); - connect(&network, SIGNAL(downloadFileProgress(const QString&, qreal)), this, SIGNAL(downloadFileProgress(const QString&, qreal))); - connect(&network, SIGNAL(downloadFileError(const QString&, const QString&)), this, SIGNAL(downloadFileError(const QString&, const QString&))); + connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse); + connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress); + connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError); } Core::Squawk::~Squawk() @@ -110,36 +110,28 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const accounts.push_back(acc); amap.insert(std::make_pair(name, acc)); - connect(acc, SIGNAL(connectionStateChanged(int)), this, SLOT(onAccountConnectionStateChanged(int))); - connect(acc, SIGNAL(error(const QString&)), this, SLOT(onAccountError(const QString&))); - connect(acc, SIGNAL(availabilityChanged(int)), this, SLOT(onAccountAvailabilityChanged(int))); - connect(acc, SIGNAL(addContact(const QString&, const QString&, const QMap&)), - this, SLOT(onAccountAddContact(const QString&, const QString&, const QMap&))); - connect(acc, SIGNAL(addGroup(const QString&)), this, SLOT(onAccountAddGroup(const QString&))); - connect(acc, SIGNAL(removeGroup(const QString&)), this, SLOT(onAccountRemoveGroup(const QString&))); - connect(acc, SIGNAL(removeContact(const QString&)), this, SLOT(onAccountRemoveContact(const QString&))); - connect(acc, SIGNAL(removeContact(const QString&, const QString&)), this, SLOT(onAccountRemoveContact(const QString&, const QString&))); - connect(acc, SIGNAL(changeContact(const QString&, const QMap&)), - this, SLOT(onAccountChangeContact(const QString&, const QMap&))); - connect(acc, SIGNAL(addPresence(const QString&, const QString&, const QMap&)), - this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap&))); - connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&))); - connect(acc, SIGNAL(message(const Shared::Message&)), this, SLOT(onAccountMessage(const Shared::Message&))); - connect(acc, SIGNAL(responseArchive(const QString&, const std::list&)), - this, SLOT(onAccountResponseArchive(const QString&, const std::list&))); + connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged); + connect(acc, &Account::changed, this, &Squawk::onAccountChanged); + connect(acc, &Account::error, this, &Squawk::onAccountError); + connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged); + connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact); + connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup); + connect(acc, &Account::removeGroup, this, &Squawk::onAccountRemoveGroup); + connect(acc, qOverload(&Account::removeContact), this, qOverload(&Squawk::onAccountRemoveContact)); + connect(acc, qOverload(&Account::removeContact), this, qOverload(&Squawk::onAccountRemoveContact)); + connect(acc, &Account::changeContact, this, &Squawk::onAccountChangeContact); + connect(acc, &Account::addPresence, this, &Squawk::onAccountAddPresence); + connect(acc, &Account::removePresence, this, &Squawk::onAccountRemovePresence); + connect(acc, &Account::message, this, &Squawk::onAccountMessage); + connect(acc, &Account::responseArchive, this, &Squawk::onAccountResponseArchive); + + connect(acc, &Account::addRoom, this, &Squawk::onAccountAddRoom); + connect(acc, &Account::changeRoom, this, &Squawk::onAccountChangeRoom); + connect(acc, &Account::removeRoom, this, &Squawk::onAccountRemoveRoom); - connect(acc, SIGNAL(addRoom(const QString&, const QMap&)), - this, SLOT(onAccountAddRoom(const QString&, const QMap&))); - connect(acc, SIGNAL(changeRoom(const QString&, const QMap&)), - this, SLOT(onAccountChangeRoom(const QString&, const QMap&))); - connect(acc, SIGNAL(removeRoom(const QString&)), this, SLOT(onAccountRemoveRoom(const QString&))); - - connect(acc, SIGNAL(addRoomParticipant(const QString&, const QString&, const QMap&)), - this, SLOT(onAccountAddRoomPresence(const QString&, const QString&, const QMap&))); - connect(acc, SIGNAL(changeRoomParticipant(const QString&, const QString&, const QMap&)), - this, SLOT(onAccountChangeRoomPresence(const QString&, const QString&, const QMap&))); - connect(acc, SIGNAL(removeRoomParticipant(const QString&, const QString&)), - this, SLOT(onAccountRemoveRoomPresence(const QString&, const QString&))); + connect(acc, &Account::addRoomParticipant, this, &Squawk::onAccountAddRoomPresence); + connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence); + connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence); QMap map = { @@ -150,8 +142,10 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const {"resource", resource}, {"state", Shared::disconnected}, {"offline", Shared::offline}, - {"error", ""} + {"error", ""}, + {"avatarPath", acc->getAvatarPath()} }; + emit newAccount(map); } @@ -263,6 +257,12 @@ void Core::Squawk::onAccountAvailabilityChanged(int state) emit changeAccount(acc->getName(), {{"availability", state}}); } +void Core::Squawk::onAccountChanged(const QMap& data) +{ + Account* acc = static_cast(sender()); + emit changeAccount(acc->getName(), data); +} + void Core::Squawk::onAccountMessage(const Shared::Message& data) { Account* acc = static_cast(sender()); diff --git a/core/squawk.h b/core/squawk.h index d6b5d2a..9435ef9 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -23,7 +23,8 @@ #include #include #include -#include +#include + #include #include "account.h" @@ -106,6 +107,7 @@ private: private slots: void onAccountConnectionStateChanged(int state); void onAccountAvailabilityChanged(int state); + void onAccountChanged(const QMap& data); void onAccountAddGroup(const QString& name); void onAccountError(const QString& text); void onAccountRemoveGroup(const QString& name); diff --git a/ui/models/account.cpp b/ui/models/account.cpp index b8758c2..9a30db7 100644 --- a/ui/models/account.cpp +++ b/ui/models/account.cpp @@ -26,6 +26,7 @@ Models::Account::Account(const QMap& data, Models::Item* pare server(data.value("server").toString()), resource(data.value("resource").toString()), error(data.value("error").toString()), + avatarPath(data.value("avatarPath").toString()), state(Shared::disconnected), availability(Shared::offline) { @@ -162,6 +163,8 @@ QVariant Models::Account::data(int column) const return QCoreApplication::translate("Global", Shared::availabilityNames[availability].toLatin1()); case 7: return resource; + case 8: + return avatarPath; default: return QVariant(); } @@ -169,7 +172,7 @@ QVariant Models::Account::data(int column) const int Models::Account::columnCount() const { - return 8; + return 9; } void Models::Account::update(const QString& field, const QVariant& value) @@ -190,6 +193,8 @@ void Models::Account::update(const QString& field, const QVariant& value) setResource(value.toString()); } else if (field == "error") { setError(value.toString()); + } else if (field == "avatarPath") { + setAvatarPath(value.toString()); } } @@ -224,3 +229,15 @@ void Models::Account::toOfflineState() setAvailability(Shared::offline); Item::toOfflineState(); } + +QString Models::Account::getAvatarPath() +{ + return avatarPath; +} + +void Models::Account::setAvatarPath(const QString& path) +{ + avatarPath = path; + changed(8); //it's uncoditional because the path doesn't change when one avatar of the same type replaces another, sha1 sums checks are on the backend +} + diff --git a/ui/models/account.h b/ui/models/account.h index fbdf204..8be7c45 100644 --- a/ui/models/account.h +++ b/ui/models/account.h @@ -50,6 +50,9 @@ namespace Models { void setError(const QString& p_resource); QString getError() const; + void setAvatarPath(const QString& path); + QString getAvatarPath(); + void setAvailability(Shared::Availability p_avail); void setAvailability(unsigned int p_avail); Shared::Availability getAvailability() const; @@ -67,6 +70,7 @@ namespace Models { QString server; QString resource; QString error; + QString avatarPath; Shared::ConnectionState state; Shared::Availability availability; diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 5f4ba21..9b5b436 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -25,14 +25,26 @@ Models::Contact::Contact(const QString& p_jid ,const QMap &da jid(p_jid), availability(Shared::offline), state(Shared::none), + avatarState(Shared::Avatar::empty), presences(), messages(), - childMessages(0) + childMessages(0), + status(), + avatarPath() { QMap::const_iterator itr = data.find("state"); if (itr != data.end()) { 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() @@ -100,7 +112,7 @@ void Models::Contact::setStatus(const QString& p_state) int Models::Contact::columnCount() const { - return 6; + return 8; } QVariant Models::Contact::data(int column) const @@ -118,6 +130,10 @@ QVariant Models::Contact::data(int column) const return getMessagesCount(); case 5: return getStatus(); + case 6: + return static_cast(getAvatarState()); + case 7: + return getAvatarPath(); default: return QVariant(); } @@ -142,6 +158,10 @@ void Models::Contact::update(const QString& field, const QVariant& value) setAvailability(value.toUInt()); } else if (field == "state") { setState(value.toUInt()); + } else if (field == "avatarState") { + setAvatarState(value.toUInt()); + } else if (field == "avatarPath") { + setAvatarPath(value.toString()); } } @@ -348,3 +368,39 @@ Models::Contact::Contact(const Models::Contact& other): 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(Shared::Avatar::valid)) { + Shared::Avatar state = static_cast(p_state); + setAvatarState(state); + } else { + qDebug() << "An attempt to set invalid avatar state" << p_state << "to the contact" << jid << ", skipping"; + } +} diff --git a/ui/models/contact.h b/ui/models/contact.h index 6f0a5fc..fda893f 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -40,6 +40,8 @@ public: QString getJid() const; Shared::Availability getAvailability() const; Shared::SubscriptionState getState() const; + Shared::Avatar getAvatarState() const; + QString getAvatarPath() const; QIcon getStatusIcon(bool big = false) const; int columnCount() const override; @@ -75,6 +77,9 @@ protected: void setAvailability(unsigned int p_state); void setState(Shared::SubscriptionState 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); @@ -82,10 +87,12 @@ private: QString jid; Shared::Availability availability; Shared::SubscriptionState state; + Shared::Avatar avatarState; QMap presences; Messages messages; unsigned int childMessages; QString status; + QString avatarPath; }; } diff --git a/ui/models/item.cpp b/ui/models/item.cpp index 7c0cb96..7ab877a 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -141,7 +141,7 @@ const Models::Item * Models::Item::parentItemConst() const int Models::Item::columnCount() const { - return 1; + return 2; } QString Models::Item::getName() const diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index f252583..5ea5b2e 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -68,6 +68,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: { + if (index.column() != 0) { + break; + } switch (item->type) { case Item::group: { Group* gr = static_cast(item); @@ -91,26 +94,50 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const case Qt::DecorationRole: switch (item->type) { case Item::account: { + quint8 col = index.column(); Account* acc = static_cast(item); - result = acc->getStatusIcon(false); + if (col == 0) { + result = acc->getStatusIcon(false); + } else if (col == 1) { + QString path = acc->getAvatarPath(); + if (path.size() > 0) { + result = QIcon(path); + } + } } break; case Item::contact: { Contact* contact = static_cast(item); - result = contact->getStatusIcon(false); + quint8 col = index.column(); + if (col == 0) { + result = contact->getStatusIcon(false); + } else if (col == 1) { + if (contact->getAvatarState() != Shared::Avatar::empty) { + result = QIcon(contact->getAvatarPath()); + } + } } break; case Item::presence: { + if (index.column() != 0) { + break; + } Presence* presence = static_cast(item); result = presence->getStatusIcon(false); } break; case Item::room: { + if (index.column() != 0) { + break; + } Room* room = static_cast(item); result = room->getStatusIcon(false); } break; case Item::participant: { + if (index.column() != 0) { + break; + } Participant* p = static_cast(item); result = p->getStatusIcon(false); } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index e382ebd..455812f 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -34,6 +34,10 @@ Squawk::Squawk(QWidget *parent) : m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->roster->setColumnWidth(1, 20); + m_ui->roster->setIconSize(QSize(20, 20)); + m_ui->roster->header()->setStretchLastSection(false); + m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch); for (unsigned int i = Shared::availabilityLowest; i < Shared::availabilityHighest + 1; ++i) { Shared::Availability av = static_cast(i); diff --git a/ui/squawk.ui b/ui/squawk.ui index 642ded2..04cf999 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -51,6 +51,9 @@ QFrame::Sunken + + true + false @@ -122,7 +125,8 @@ false - + + .. Add conference From 46e74ad5e8cf5f842bbf082a7542dbfff7728baa Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 17 Oct 2019 23:54:27 +0300 Subject: [PATCH 04/20] initial vCard class, signal-slots refactoring --- global.cpp | 57 +++++++++++++++++++++++++++ global.h | 72 ++++++++++++++++++++++++++++++++++ main.cpp | 113 +++++++++++++++++++++++------------------------------ 3 files changed, 177 insertions(+), 65 deletions(-) diff --git a/global.cpp b/global.cpp index 8ab74d8..e9156fd 100644 --- a/global.cpp +++ b/global.cpp @@ -296,6 +296,63 @@ bool Shared::Message::storable() const return id.size() > 0 && (body.size() > 0 || oob.size()) > 0; } +Shared::VCard::Contact::Contact(Shared::VCard::Contact::Role p_role, bool p_prefered): + role(p_role), + prefered(p_prefered) +{} + +Shared::VCard::Email::Email(const QString& addr, Shared::VCard::Contact::Role p_role, bool p_prefered): + Contact(p_role, p_prefered), + address(addr) +{} + +Shared::VCard::Phone::Phone(const QString& nmbr, Shared::VCard::Phone::Type p_type, Shared::VCard::Contact::Role p_role, bool p_prefered): + Contact(p_role, p_prefered), + number(nmbr), + type(p_type) +{} + +Shared::VCard::Address::Address(const QString& zCode, const QString& cntry, const QString& rgn, const QString& lclty, const QString& strt, const QString& ext, Shared::VCard::Contact::Role p_role, bool p_prefered): + Contact(p_role, p_prefered), + zipCode(zCode), + country(cntry), + region(rgn), + locality(lclty), + street(strt), + external(ext) +{} + +Shared::VCard::VCard(): + firstName(), + middleName(), + lastName(), + nickName(), + description(), + birthday(), + photoType(Avatar::empty), + photoPath(), + receivingTime(QDateTime::currentDateTime()), + emails(), + phones(), + addresses() +{} + +Shared::VCard::VCard(const QDateTime& creationTime): + firstName(), + middleName(), + lastName(), + nickName(), + description(), + birthday(), + photoType(Avatar::empty), + photoPath(), + receivingTime(creationTime), + emails(), + phones(), + addresses() +{ +} + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index ab84655..e0acd91 100644 --- a/global.h +++ b/global.h @@ -215,6 +215,78 @@ private: QString oob; }; +class VCard { + class Contact { + public: + enum Role { + none, + home, + work + }; + + Contact(Role p_role = none, bool p_prefered = false); + + Role role; + bool prefered; + }; +public: + class Email : public Contact { + public: + Email(const QString& address, Role p_role = none, bool p_prefered = false); + + QString address; + }; + class Phone : public Contact { + enum Type { + fax, + pager, + voice, + cell, + video, + modem + }; + Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false); + + QString number; + Type type; + }; + class Address : public Contact { + Address( + const QString& zCode = "", + const QString& cntry = "", + const QString& rgn = "", + const QString& lclty = "", + const QString& strt = "", + const QString& ext = "", + Role p_role = none, + bool p_prefered = false + ); + + QString zipCode; + QString country; + QString region; + QString locality; + QString street; + QString external; + }; + VCard(); + VCard(const QDateTime& creationTime); + +private: + QString firstName; + QString middleName; + QString lastName; + QString nickName; + QString description; + QDate birthday; + Avatar photoType; + QString photoPath; + QDateTime receivingTime; + std::deque emails; + std::deque phones; + std::deque
addresses; +}; + static const std::deque fallbackAvailabilityThemeIconsLightBig = { ":images/fallback/light/big/online.svg", ":images/fallback/light/big/away.svg", diff --git a/main.cpp b/main.cpp index 0b8c9a4..83db32a 100644 --- a/main.cpp +++ b/main.cpp @@ -30,6 +30,7 @@ int main(int argc, char *argv[]) { qRegisterMetaType("Shared::Message"); + qRegisterMetaType("Shared::VCard"); qRegisterMetaType>("std::list"); qRegisterMetaType>("QSet"); @@ -78,76 +79,58 @@ int main(int argc, char *argv[]) QThread* coreThread = new QThread(); squawk->moveToThread(coreThread); - QObject::connect(coreThread, SIGNAL(started()), squawk, SLOT(start())); - QObject::connect(&app, SIGNAL(aboutToQuit()), squawk, SLOT(stop())); - QObject::connect(squawk, SIGNAL(quit()), coreThread, SLOT(quit())); - QObject::connect(coreThread, SIGNAL(finished()), squawk, SLOT(deleteLater())); + QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); + QObject::connect(&app, &QApplication::aboutToQuit, squawk, &Core::Squawk::stop); + QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit); + QObject::connect(coreThread, &QThread::finished, squawk, &Core::Squawk::deleteLater); - QObject::connect(&w, SIGNAL(newAccountRequest(const QMap&)), squawk, SLOT(newAccountRequest(const QMap&))); - QObject::connect(&w, SIGNAL(modifyAccountRequest(const QString&, const QMap&)), - squawk, SLOT(modifyAccountRequest(const QString&, const QMap&))); - QObject::connect(&w, SIGNAL(removeAccountRequest(const QString&)), squawk, SLOT(removeAccountRequest(const QString&))); - QObject::connect(&w, SIGNAL(connectAccount(const QString&)), squawk, SLOT(connectAccount(const QString&))); - QObject::connect(&w, SIGNAL(disconnectAccount(const QString&)), squawk, SLOT(disconnectAccount(const QString&))); - QObject::connect(&w, SIGNAL(changeState(int)), squawk, SLOT(changeState(int))); - QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&)), squawk, SLOT(sendMessage(const QString&, const Shared::Message&))); - QObject::connect(&w, SIGNAL(requestArchive(const QString&, const QString&, int, const QString&)), - squawk, SLOT(requestArchive(const QString&, const QString&, int, const QString&))); - QObject::connect(&w, SIGNAL(subscribeContact(const QString&, const QString&, const QString&)), - squawk, SLOT(subscribeContact(const QString&, const QString&, const QString&))); - QObject::connect(&w, SIGNAL(unsubscribeContact(const QString&, const QString&, const QString&)), - squawk, SLOT(unsubscribeContact(const QString&, const QString&, const QString&))); - QObject::connect(&w, SIGNAL(addContactRequest(const QString&, const QString&, const QString&, const QSet&)), - squawk, SLOT(addContactRequest(const QString&, const QString&, const QString&, const QSet&))); - QObject::connect(&w, SIGNAL(removeContactRequest(const QString&, const QString&)), - squawk, SLOT(removeContactRequest(const QString&, const QString&))); - QObject::connect(&w, SIGNAL(setRoomJoined(const QString&, const QString&, bool)), squawk, SLOT(setRoomJoined(const QString&, const QString&, bool))); - QObject::connect(&w, SIGNAL(setRoomAutoJoin(const QString&, const QString&, bool)), squawk, SLOT(setRoomAutoJoin(const QString&, const QString&, bool))); - - QObject::connect(&w, SIGNAL(removeRoomRequest(const QString&, const QString&)), - squawk, SLOT(removeRoomRequest(const QString&, const QString&))); - QObject::connect(&w, SIGNAL(addRoomRequest(const QString&, const QString&, const QString&, const QString&, bool)), - squawk, SLOT(addRoomRequest(const QString&, const QString&, const QString&, const QString&, bool))); - QObject::connect(&w, SIGNAL(fileLocalPathRequest(const QString&, const QString&)), squawk, SLOT(fileLocalPathRequest(const QString&, const QString&))); - QObject::connect(&w, SIGNAL(downloadFileRequest(const QString&, const QString&)), squawk, SLOT(downloadFileRequest(const QString&, const QString&))); + QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest); + QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest); + QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest); + QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount); + QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount); + QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState); + QObject::connect(&w, &Squawk::sendMessage, squawk, &Core::Squawk::sendMessage); + QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive); + QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact); + QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact); + QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest); + QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest); + QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined); + QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin); + QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest); + QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest); + QObject::connect(&w, &Squawk::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::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest); QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest); - QObject::connect(squawk, SIGNAL(newAccount(const QMap&)), &w, SLOT(newAccount(const QMap&))); - QObject::connect(squawk, SIGNAL(addContact(const QString&, const QString&, const QString&, const QMap&)), - &w, SLOT(addContact(const QString&, const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(changeAccount(const QString&, const QMap&)), - &w, SLOT(changeAccount(const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(removeAccount(const QString&)), &w, SLOT(removeAccount(const QString&))); - QObject::connect(squawk, SIGNAL(addGroup(const QString&, const QString&)), &w, SLOT(addGroup(const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(removeGroup(const QString&, const QString&)), &w, SLOT(removeGroup(const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(changeContact(const QString&, const QString&, const QMap&)), - &w, SLOT(changeContact(const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(addPresence(const QString&, const QString&, const QString&, const QMap&)), - &w, SLOT(addPresence(const QString&, const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(removePresence(const QString&, const QString&, const QString&)), &w, SLOT(removePresence(const QString&, const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(stateChanged(int)), &w, SLOT(stateChanged(int))); - QObject::connect(squawk, SIGNAL(accountMessage(const QString&, const Shared::Message&)), &w, SLOT(accountMessage(const QString&, const Shared::Message&))); - QObject::connect(squawk, SIGNAL(responseArchive(const QString&, const QString&, const std::list&)), - &w, SLOT(responseArchive(const QString&, const QString&, const std::list&))); - - QObject::connect(squawk, SIGNAL(addRoom(const QString&, const QString&, const QMap&)), - &w, SLOT(addRoom(const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(changeRoom(const QString&, const QString&, const QMap&)), - &w, SLOT(changeRoom(const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(removeRoom(const QString&, const QString&)), &w, SLOT(removeRoom(const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(addRoomParticipant(const QString&, const QString&, const QString&, const QMap&)), - &w, SLOT(addRoomParticipant(const QString&, const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(changeRoomParticipant(const QString&, const QString&, const QString&, const QMap&)), - &w, SLOT(changeRoomParticipant(const QString&, const QString&, const QString&, const QMap&))); - QObject::connect(squawk, SIGNAL(removeRoomParticipant(const QString&, const QString&, const QString&)), - &w, SLOT(removeRoomParticipant(const QString&, const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), &w, SLOT(fileLocalPathResponse(const QString&, const QString&))); - QObject::connect(squawk, SIGNAL(downloadFileProgress(const QString&, qreal)), &w, SLOT(downloadFileProgress(const QString&, qreal))); - QObject::connect(squawk, SIGNAL(downloadFileError(const QString&, const QString&)), &w, SLOT(downloadFileError(const QString&, const QString&))); + QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount); + QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact); + QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount); + QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount); + QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup); + QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup); + QObject::connect(squawk, qOverload(&Core::Squawk::removeContact), + &w, qOverload(&Squawk::removeContact)); + QObject::connect(squawk, qOverload(&Core::Squawk::removeContact), + &w, qOverload(&Squawk::removeContact)); + QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact); + QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence); + QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence); + QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged); + QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage); + QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive); + QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom); + QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom); + QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom); + QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant); + QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant); + QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant); + QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse); + QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::downloadFileProgress); + QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::downloadFileError); //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); From d050cd82dde47e1b74c6bcd4b34cd87fbb4afb35 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 19 Oct 2019 22:34:25 +0300 Subject: [PATCH 05/20] some lines to vCard object --- core/account.cpp | 38 +++++++++++++++++++ core/account.h | 2 + global.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ global.h | 17 +++++++++ 4 files changed, 153 insertions(+) diff --git a/core/account.cpp b/core/account.cpp index a14e60f..1da14a3 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -1341,6 +1341,7 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) if (confItr == conferences.end()) { if (jid == getLogin() + "@" + getServer()) { onOwnVCardReceived(card); + } else { qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; } @@ -1353,11 +1354,32 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) } QByteArray ava = card.photo(); + if (ava.size() > 0) { item->setAvatar(ava); } else { item->setAutoGeneratedAvatar(); } + + Shared::VCard vCard; + vCard.setFirstName(card.firstName()); + vCard.setMiddleName(card.middleName()); + vCard.setLastName(card.lastName()); + vCard.setBirthday(card.birthday()); + vCard.setNickName(card.nickName()); + vCard.setDescription(card.description()); + if (item->hasAvatar()) { + if (item->isAvatarAutoGenerated()) { + vCard.setAvatarType(Shared::Avatar::valid); + } else { + vCard.setAvatarType(Shared::Avatar::autocreated); + } + vCard.setAvatarPath(item->avatarPath()); + } else { + vCard.setAvatarType(Shared::Avatar::empty); + } + + emit receivedVCard(jid, vCard); } void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) @@ -1438,6 +1460,22 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) } ownVCardRequestInProgress = false; + + Shared::VCard vCard; + vCard.setFirstName(card.firstName()); + vCard.setMiddleName(card.middleName()); + vCard.setLastName(card.lastName()); + vCard.setBirthday(card.birthday()); + vCard.setNickName(card.nickName()); + vCard.setDescription(card.description()); + if (avatarType.size() > 0) { + vCard.setAvatarType(Shared::Avatar::valid); + vCard.setAvatarPath(path + "avatar." + avatarType); + } else { + vCard.setAvatarType(Shared::Avatar::empty); + } + + emit receivedVCard(getLogin() + "@" + getServer(), vCard); } QString Core::Account::getAvatarPath() const diff --git a/core/account.h b/core/account.h index f9d8469..b5b8c46 100644 --- a/core/account.h +++ b/core/account.h @@ -87,6 +87,7 @@ public: void setRoomAutoJoin(const QString& jid, bool joined); void removeRoomRequest(const QString& jid); void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); + void requestVCard(const QString& jid); signals: void changed(const QMap& data); @@ -109,6 +110,7 @@ signals: void addRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void removeRoomParticipant(const QString& jid, const QString& nickName); + void receivedVCard(const QString& jid, const Shared::VCard& card); private: QString name; diff --git a/global.cpp b/global.cpp index e9156fd..fa63539 100644 --- a/global.cpp +++ b/global.cpp @@ -353,6 +353,102 @@ Shared::VCard::VCard(const QDateTime& creationTime): { } +QString Shared::VCard::getAvatarPath() const +{ + return photoPath; +} + +Shared::Avatar Shared::VCard::getAvatarType() const +{ + return photoType; +} + +QDate Shared::VCard::getBirthday() const +{ + return birthday; +} + +QString Shared::VCard::getDescription() const +{ + return description; +} + +QString Shared::VCard::getFirstName() const +{ + return firstName; +} + +QString Shared::VCard::getLastName() const +{ + return lastName; +} + +QString Shared::VCard::getMiddleName() const +{ + return middleName; +} + +QString Shared::VCard::getNickName() const +{ + return nickName; +} + +void Shared::VCard::setAvatarPath(const QString& path) +{ + if (path != photoPath) { + photoPath = path; + } +} + +void Shared::VCard::setAvatarType(Shared::Avatar type) +{ + if (photoType != type) { + photoType = type; + } +} + +void Shared::VCard::setBirthday(const QDate& date) +{ + if (date.isValid() && birthday != date) { + birthday = date; + } +} + +void Shared::VCard::setDescription(const QString& descr) +{ + if (description != descr) { + description = descr; + } +} + +void Shared::VCard::setFirstName(const QString& first) +{ + if (firstName != first) { + firstName = first; + } +} + +void Shared::VCard::setLastName(const QString& last) +{ + if (lastName != last) { + lastName = last; + } +} + +void Shared::VCard::setMiddleName(const QString& middle) +{ + if (middleName != middle) { + middleName = middle; + } +} + +void Shared::VCard::setNickName(const QString& nick) +{ + if (nickName != nick) { + nickName = nick; + } +} + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index e0acd91..b20e616 100644 --- a/global.h +++ b/global.h @@ -272,6 +272,23 @@ public: VCard(); VCard(const QDateTime& creationTime); + QString getFirstName() const; + void setFirstName(const QString& first); + QString getMiddleName() const; + void setMiddleName(const QString& middle); + QString getLastName() const; + void setLastName(const QString& last); + QString getNickName() const; + void setNickName(const QString& nick); + QString getDescription() const; + void setDescription(const QString& descr); + QDate getBirthday() const; + void setBirthday(const QDate& date); + Avatar getAvatarType() const; + void setAvatarType(Avatar type); + QString getAvatarPath() const; + void setAvatarPath(const QString& path); + private: QString firstName; QString middleName; From e7be046e9f5af7eba6f33bc77a75db5c53ee0991 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 20 Oct 2019 22:39:11 +0300 Subject: [PATCH 06/20] vcard ui file --- ui/CMakeLists.txt | 1 + ui/widgets/vcard.cpp | 31 ++ ui/widgets/vcard.h | 44 +++ ui/widgets/vcard.ui | 737 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 813 insertions(+) create mode 100644 ui/widgets/vcard.cpp create mode 100644 ui/widgets/vcard.h create mode 100644 ui/widgets/vcard.ui diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 982bb9a..0f6680a 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -29,6 +29,7 @@ set(squawkUI_SRC widgets/accounts.cpp widgets/account.cpp widgets/joinconference.cpp + widgets/vcard.cpp utils/messageline.cpp utils//message.cpp utils/resizer.cpp diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp new file mode 100644 index 0000000..f2db0e9 --- /dev/null +++ b/ui/widgets/vcard.cpp @@ -0,0 +1,31 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "vcard.h" +#include "ui_vcard.h" + +VCard::VCard(QWidget* parent): + QWidget(parent), + m_ui(new Ui::VCard()) +{ + m_ui->setupUi(this); +} + +VCard::~VCard() +{ +} diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard.h new file mode 100644 index 0000000..c9fe19d --- /dev/null +++ b/ui/widgets/vcard.h @@ -0,0 +1,44 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef VCARD_H +#define VCARD_H + +#include +#include + +namespace Ui +{ +class VCard; +} + +/** + * @todo write docs + */ +class VCard : public QWidget +{ + Q_OBJECT +public: + VCard(QWidget* parent = nullptr); + ~VCard(); + +private: + QScopedPointer m_ui; +}; + +#endif // VCARD_H diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui new file mode 100644 index 0000000..5658d2c --- /dev/null +++ b/ui/widgets/vcard.ui @@ -0,0 +1,737 @@ + + + VCard + + + + 0 + 0 + 434 + 534 + + + + + 6 + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Qt::TabFocus + + + QTabWidget::North + + + QTabWidget::Rounded + + + 0 + + + Qt::ElideNone + + + true + + + false + + + + General + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Personal information</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Middle name + + + middleName + + + + + + + First name + + + firstName + + + + + + + Last name + + + lastName + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Nick name + + + nickName + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Birthday + + + birthday + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Organization</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Name + + + organizationName + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Department + + + organizationDepartment + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Role + + + organizationRole + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Title + + + organizationTitle + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + + + Qt::Horizontal + + + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">General</span></p></body></html> + + + + + + + + Contact + + + + 0 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Contact</span></p></body></html> + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + true + + + + + 0 + 0 + 422 + 410 + + + + + + + Qt::Horizontal + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + JabberID + + + jabberID + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Description + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Description</span></p></body></html> + + + + + + + QFrame::StyledPanel + + + Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Save + + + false + + + + + + + firstName + middleName + lastName + nickName + birthday + organizationName + organizationDepartment + organizationRole + organizationTitle + scrollArea + jabberID + description + tabWidget + + + + + + From c4d22c9c1429e799cbebff8004baa7c82df94ac4 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 21 Oct 2019 18:02:41 +0300 Subject: [PATCH 07/20] some methods to cVard --- ui/utils/image.cpp | 37 ++++++++++++++++++------- ui/utils/image.h | 28 ++++++------------- ui/widgets/vcard.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++-- ui/widgets/vcard.h | 14 +++++++++- ui/widgets/vcard.ui | 33 ++++++++++++++++++---- 5 files changed, 139 insertions(+), 38 deletions(-) diff --git a/ui/utils/image.cpp b/ui/utils/image.cpp index 0abccf1..6a2ce1c 100644 --- a/ui/utils/image.cpp +++ b/ui/utils/image.cpp @@ -19,19 +19,13 @@ #include #include "image.h" -Image::Image(const QString& path, QWidget* parent): +Image::Image(const QString& path, quint16 p_minWidth, QWidget* parent): QLabel(parent), pixmap(path), - aspectRatio(0) + aspectRatio(0), + minWidth(p_minWidth) { - - qreal height = pixmap.height(); - qreal width = pixmap.width(); - aspectRatio = width / height; - setPixmap(pixmap); setScaledContents(true); - setMinimumHeight(50 / aspectRatio); - setMinimumWidth(50); } Image::~Image() @@ -42,7 +36,6 @@ Image::~Image() int Image::heightForWidth(int width) const { int height = width / aspectRatio; - //qDebug() << height << width << aspectRatio; return height; } @@ -50,3 +43,27 @@ bool Image::hasHeightForWidth() const { return true; } + +void Image::recalculateAspectRatio() +{ + qreal height = pixmap.height(); + qreal width = pixmap.width(); + aspectRatio = width / height; + setPixmap(pixmap); + setMinimumHeight(minWidth / aspectRatio); + setMinimumWidth(minWidth); +} + +void Image::setMinWidth(quint16 p_minWidth) +{ + if (minWidth != p_minWidth) { + minWidth = p_minWidth; + recalculateAspectRatio(); + } +} + +void Image::setPath(const QString& path) +{ + pixmap = QPixmap(path); + recalculateAspectRatio(); +} diff --git a/ui/utils/image.h b/ui/utils/image.h index 7d61202..a583f94 100644 --- a/ui/utils/image.h +++ b/ui/utils/image.h @@ -28,34 +28,22 @@ class Image : public QLabel { public: - /** - * Default constructor - */ - Image(const QString& path, QWidget* parent = nullptr); + Image(const QString& path, quint16 minWidth = 50, QWidget* parent = nullptr); - /** - * Destructor - */ ~Image(); - /** - * @todo write docs - * - * @param TODO - * @return TODO - */ int heightForWidth(int width) const override; - - /** - * @todo write docs - * - * @return TODO - */ - virtual bool hasHeightForWidth() const; + bool hasHeightForWidth() const override; + void setPath(const QString& path); + void setMinWidth(quint16 minWidth); private: QPixmap pixmap; qreal aspectRatio; + quint16 minWidth; + +private: + void recalculateAspectRatio(); }; #endif // IMAGE_H diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index f2db0e9..7072ca1 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -19,13 +19,74 @@ #include "vcard.h" #include "ui_vcard.h" -VCard::VCard(QWidget* parent): +VCard::VCard(bool edit, QWidget* parent): QWidget(parent), - m_ui(new Ui::VCard()) + m_ui(new Ui::VCard()), + avatar(":/images/logo.svg", 64) { m_ui->setupUi(this); + + if (edit) { + + } else { + m_ui->buttonBox->hide(); + m_ui->firstName->setReadOnly(true); + m_ui->middleName->setReadOnly(true); + m_ui->lastName->setReadOnly(true); + m_ui->nickName->setReadOnly(true); + m_ui->birthday->setReadOnly(true); + m_ui->organizationName->setReadOnly(true); + m_ui->organizationDepartment->setReadOnly(true); + m_ui->organizationTitle->setReadOnly(true); + m_ui->organizationRole->setReadOnly(true); + m_ui->description->setReadOnly(true); + m_ui->jabberID->setReadOnly(true); + } + + connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, m_ui->buttonBox, &QDialogButtonBox::deleteLater); } VCard::~VCard() { } + +void VCard::setVCard(const QString& jid, const Shared::VCard& card) +{ + m_ui->jabberID->setText(jid); + m_ui->firstName->setText(card.getFirstName()); + m_ui->middleName->setText(card.getMiddleName()); + m_ui->lastName->setText(card.getLastName()); + m_ui->nickName->setText(card.getNickName()); + m_ui->birthday->setDate(card.getBirthday()); + //m_ui->organizationName->setText(card.get()); + //m_ui->organizationDepartment->setText(card.get()); + //m_ui->organizationTitle->setText(card.get()); + //m_ui->organizationRole->setText(card.get()); + m_ui->description->setText(card.getDescription()); + + QString path; + switch (card.getAvatarType()) { + case Shared::Avatar::empty: + path = QApplication::palette().window().color().lightnessF() > 0.5 ? ":/images/fallback/dark/big/user.svg" : ":/images/fallback/light/big/user.svg"; + break; + case Shared::Avatar::autocreated: + case Shared::Avatar::valid: + path = card.getAvatarPath(); + break; + } + avatar.setPath(path); +} + +void VCard::onButtonBoxAccepted() +{ + Shared::VCard card; + card.setFirstName(m_ui->firstName->text()); + card.setMiddleName(m_ui->middleName->text()); + card.setLastName(m_ui->lastName->text()); + card.setNickName(m_ui->nickName->text()); + card.setBirthday(m_ui->birthday->date()); + card.setDescription(m_ui->description->toPlainText()); + + emit saveVCard(m_ui->jabberID->text(), card); +} diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard.h index c9fe19d..dc14172 100644 --- a/ui/widgets/vcard.h +++ b/ui/widgets/vcard.h @@ -22,6 +22,9 @@ #include #include +#include "../../global.h" +#include "../utils/image.h" + namespace Ui { class VCard; @@ -34,11 +37,20 @@ class VCard : public QWidget { Q_OBJECT public: - VCard(QWidget* parent = nullptr); + VCard(bool edit = false, QWidget* parent = nullptr); ~VCard(); + void setVCard(const QString& jid, const Shared::VCard& card); + +signals: + void saveVCard(const QString& jid, const Shared::VCard& card); + +private slots: + void onButtonBoxAccepted(); + private: QScopedPointer m_ui; + Image avatar; }; #endif // VCARD_H diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui index 5658d2c..2ebae41 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard.ui @@ -6,7 +6,7 @@ 0 0 - 434 + 544 534 @@ -49,7 +49,7 @@ false - + General @@ -433,7 +433,7 @@ - + Contact @@ -479,7 +479,7 @@ 0 0 - 422 + 532 410 @@ -539,13 +539,36 @@ - JabberID + Jabber ID jabberID + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Web site + + + From 2a37f36b8394508a03b574aa7e222abea0f92142 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 22 Oct 2019 18:13:56 +0300 Subject: [PATCH 08/20] first primitive vcard in graphic interface --- core/account.cpp | 26 +++++++++----- core/squawk.cpp | 17 +++++++-- core/squawk.h | 2 ++ main.cpp | 2 ++ ui/models/account.cpp | 9 +++++ ui/models/account.h | 3 ++ ui/squawk.cpp | 63 ++++++++++++++++++++++++++++++-- ui/squawk.h | 6 ++++ ui/utils/image.cpp | 22 ++++++++++++ ui/utils/image.h | 4 +++ ui/widgets/vcard.cpp | 39 +++++++++++++++----- ui/widgets/vcard.h | 6 ++-- ui/widgets/vcard.ui | 84 +++++++++++++++++-------------------------- 13 files changed, 208 insertions(+), 75 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 1da14a3..04d2947 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -344,8 +344,7 @@ void Core::Account::addedAccount(const QString& jid) } else { cData.insert("avatarState", static_cast(Shared::Avatar::empty)); cData.insert("avatarPath", ""); - client.vCardManager().requestVCard(jid); - pendingVCardRequests.insert(jid); + requestVCard(jid); } int grCount = 0; for (QSet::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) { @@ -442,17 +441,14 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load if (cnt->hasAvatar()) { if (cnt->isAvatarAutoGenerated()) { - client.vCardManager().requestVCard(jid); - pendingVCardRequests.insert(jid); + requestVCard(jid); } else { if (cnt->avatarHash() != p_presence.photoHash()) { - client.vCardManager().requestVCard(jid); - pendingVCardRequests.insert(jid); + requestVCard(jid); } } } else { - client.vCardManager().requestVCard(jid); - pendingVCardRequests.insert(jid); + requestVCard(jid); } break; } @@ -1494,3 +1490,17 @@ void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& p emit changeContact(item->jid, cData); } +void Core::Account::requestVCard(const QString& jid) +{ + if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { + if (jid == getLogin() + "@" + getServer()) { + if (!ownVCardRequestInProgress) { + client.vCardManager().requestClientVCard(); + ownVCardRequestInProgress = true; + } + } else { + client.vCardManager().requestVCard(jid); + pendingVCardRequests.insert(jid); + } + } +} diff --git a/core/squawk.cpp b/core/squawk.cpp index 61b624d..35977a4 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -133,6 +133,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence); connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence); + connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); QMap map = { {"login", login}, @@ -507,7 +508,7 @@ void Core::Squawk::addContactToGroupRequest(const QString& account, const QStrin { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to add contact" << jid << "of existing account" << account << "to the group" << groupName << ", skipping"; + qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; return; } itr->second->addContactToGroupRequest(jid, groupName); @@ -517,7 +518,7 @@ void Core::Squawk::removeContactFromGroupRequest(const QString& account, const Q { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to add contact" << jid << "of existing account" << account << "to the group" << groupName << ", skipping"; + qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; return; } itr->second->removeContactFromGroupRequest(jid, groupName); @@ -527,8 +528,18 @@ void Core::Squawk::renameContactRequest(const QString& account, const QString& j { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to rename contact" << jid << "of existing account" << account << ", skipping"; + qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping"; return; } itr->second->renameContactRequest(jid, newName); } + +void Core::Squawk::requestVCard(const QString& account, const QString& jid) +{ + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping"; + return; + } + itr->second->requestVCard(jid); +} diff --git a/core/squawk.h b/core/squawk.h index 9435ef9..9176f28 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -66,6 +66,7 @@ signals: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileError(const QString& messageId, const QString& error); void downloadFileProgress(const QString& messageId, qreal value); + void responseVCard(const QString& jid, const Shared::VCard& card); public slots: void start(); @@ -91,6 +92,7 @@ public slots: void removeRoomRequest(const QString& account, const QString& jid); void fileLocalPathRequest(const QString& messageId, const QString& url); void downloadFileRequest(const QString& messageId, const QString& url); + void requestVCard(const QString& account, const QString& jid); private: typedef std::deque Accounts; diff --git a/main.cpp b/main.cpp index 83db32a..49a9875 100644 --- a/main.cpp +++ b/main.cpp @@ -105,6 +105,7 @@ int main(int argc, char *argv[]) QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest); QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest); QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest); + QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard); QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount); QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact); @@ -131,6 +132,7 @@ int main(int argc, char *argv[]) QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse); QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::downloadFileProgress); QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::downloadFileError); + QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard); //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); diff --git a/ui/models/account.cpp b/ui/models/account.cpp index 9a30db7..eeb8731 100644 --- a/ui/models/account.cpp +++ b/ui/models/account.cpp @@ -241,3 +241,12 @@ void Models::Account::setAvatarPath(const QString& path) changed(8); //it's uncoditional because the path doesn't change when one avatar of the same type replaces another, sha1 sums checks are on the backend } +QString Models::Account::getBareJid() const +{ + return login + "@" + server; +} + +QString Models::Account::getFullJid() const +{ + return getBareJid() + "/" + resource; +} diff --git a/ui/models/account.h b/ui/models/account.h index 8be7c45..e114699 100644 --- a/ui/models/account.h +++ b/ui/models/account.h @@ -64,6 +64,9 @@ namespace Models { void update(const QString& field, const QVariant& value); + QString getBareJid() const; + QString getFullJid() const; + private: QString login; QString password; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 455812f..051e691 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -29,7 +29,9 @@ Squawk::Squawk(QWidget *parent) : rosterModel(), conversations(), 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() { m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); @@ -136,12 +138,19 @@ void Squawk::closeEvent(QCloseEvent* event) if (accounts != 0) { accounts->close(); } + for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { - disconnect(itr->second, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); + disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed); itr->second->close(); } conversations.clear(); + for (std::map::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) { + disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed); + itr->second->close(); + } + vCards.clear(); + QMainWindow::closeEvent(event); } @@ -542,6 +551,10 @@ void Squawk::onRosterContextMenu(const QPoint& point) }); } + QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); + card->setEnabled(active); + connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true)); + QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); connect(remove, &QAction::triggered, [this, name]() { @@ -636,6 +649,10 @@ void Squawk::onRosterContextMenu(const QPoint& point) }); + QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); + card->setEnabled(active); + connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, accName, cnt->getJid(), false)); + QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); connect(remove, &QAction::triggered, [this, cnt]() { @@ -721,3 +738,45 @@ void Squawk::removeRoomParticipant(const QString& account, const QString& jid, c { rosterModel.removeRoomParticipant(account, jid, name); } + +void Squawk::responseVCard(const QString& jid, const Shared::VCard& card) +{ + std::map::const_iterator itr = vCards.find(jid); + if (itr != vCards.end()) { + itr->second->setVCard(card); + } +} + +void Squawk::onVCardClosed() +{ + VCard* vCard = static_cast(sender()); + + std::map::const_iterator itr = vCards.find(vCard->getJid()); + if (itr == vCards.end()) { + qDebug() << "VCard has been closed but can not be found among other opened vCards, application is most probably going to crash"; + return; + } + vCards.erase(itr); +} + +void Squawk::onActivateVCard(const QString& account, const QString& jid, bool edition) +{ + std::map::const_iterator itr = vCards.find(jid); + VCard* card; + Models::Contact::Messages deque; + if (itr != vCards.end()) { + card = itr->second; + } else { + card = new VCard(jid, edition); + card->setAttribute(Qt::WA_DeleteOnClose); + vCards.insert(std::make_pair(jid, card)); + + connect(card, &VCard::destroyed, this, &Squawk::onVCardClosed); + } + + card->show(); + card->raise(); + card->activateWindow(); + + emit requestVCard(account, jid); +} diff --git a/ui/squawk.h b/ui/squawk.h index c4d7f6f..7308882 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -33,6 +33,7 @@ #include "widgets/room.h" #include "widgets/newcontact.h" #include "widgets/joinconference.h" +#include "widgets/vcard.h" #include "models/roster.h" #include "../global.h" @@ -71,6 +72,7 @@ signals: void removeRoomRequest(const QString& account, const QString& jid); void fileLocalPathRequest(const QString& messageId, const QString& url); void downloadFileRequest(const QString& messageId, const QString& url); + void requestVCard(const QString& account, const QString& jid); public slots: void newAccount(const QMap& account); @@ -96,6 +98,7 @@ public slots: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileError(const QString& messageId, const QString& error); void downloadFileProgress(const QString& messageId, qreal value); + void responseVCard(const QString& jid, const Shared::VCard& card); private: typedef std::map Conversations; @@ -107,6 +110,7 @@ private: QMenu* contextMenu; QDBusInterface dbus; std::map> requestedFiles; + std::map vCards; protected: void closeEvent(QCloseEvent * event) override; @@ -121,6 +125,8 @@ private slots: void onAccountsSizeChanged(unsigned int size); void onAccountsClosed(QObject* parent = 0); void onConversationClosed(QObject* parent = 0); + void onVCardClosed(); + void onActivateVCard(const QString& account, const QString& jid, bool edition = false); void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); void onConversationMessage(const Shared::Message& msg); diff --git a/ui/utils/image.cpp b/ui/utils/image.cpp index 6a2ce1c..dca8153 100644 --- a/ui/utils/image.cpp +++ b/ui/utils/image.cpp @@ -26,6 +26,17 @@ Image::Image(const QString& path, quint16 p_minWidth, QWidget* parent): minWidth(p_minWidth) { setScaledContents(true); + recalculateAspectRatio(); +} + +Image::Image(const QString& path, quint16 width, quint16 height, quint16 p_minWidth, QWidget* parent): + QLabel(parent), + pixmap(QIcon(path).pixmap(QSize(width, height))), + aspectRatio(0), + minWidth(p_minWidth) +{ + setScaledContents(true); + recalculateAspectRatio(); } Image::~Image() @@ -39,6 +50,11 @@ int Image::heightForWidth(int width) const return height; } +int Image::widthForHeight(int height) const +{ + return height * aspectRatio; +} + bool Image::hasHeightForWidth() const { return true; @@ -67,3 +83,9 @@ void Image::setPath(const QString& path) pixmap = QPixmap(path); recalculateAspectRatio(); } + +void Image::setPath(const QString& path, quint16 width, quint16 height) +{ + pixmap = QPixmap(QIcon(path).pixmap(QSize(width, height))); + recalculateAspectRatio(); +} diff --git a/ui/utils/image.h b/ui/utils/image.h index a583f94..82071ca 100644 --- a/ui/utils/image.h +++ b/ui/utils/image.h @@ -21,6 +21,7 @@ #include #include +#include /** * @todo write docs @@ -29,12 +30,15 @@ class Image : public QLabel { public: Image(const QString& path, quint16 minWidth = 50, QWidget* parent = nullptr); + Image(const QString& path, quint16 width, quint16 height, quint16 minWidth = 50, QWidget* parent = nullptr); ~Image(); int heightForWidth(int width) const override; + int widthForHeight(int height) const; bool hasHeightForWidth() const override; void setPath(const QString& path); + void setPath(const QString& path, quint16 width, quint16 height); void setMinWidth(quint16 minWidth); private: diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index 7072ca1..e19538a 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -19,12 +19,19 @@ #include "vcard.h" #include "ui_vcard.h" -VCard::VCard(bool edit, QWidget* parent): +VCard::VCard(const QString& jid, bool edit, QWidget* parent): QWidget(parent), m_ui(new Ui::VCard()), - avatar(":/images/logo.svg", 64) + avatar(QApplication::palette().window().color().lightnessF() > 0.5 ? ":/images/fallback/dark/big/user.svg" : ":/images/fallback/light/big/user.svg", 256, 256, 256) { m_ui->setupUi(this); + m_ui->jabberID->setText(jid); + m_ui->jabberID->setReadOnly(true); + QGridLayout* general = static_cast(m_ui->General->layout()); + general->addWidget(&avatar, 2, 2, 1, 1); + avatar.setFrameShape(QFrame::StyledPanel); + avatar.setFrameShadow(QFrame::Sunken); + avatar.setMargin(6); if (edit) { @@ -40,11 +47,15 @@ VCard::VCard(bool edit, QWidget* parent): m_ui->organizationTitle->setReadOnly(true); m_ui->organizationRole->setReadOnly(true); m_ui->description->setReadOnly(true); - m_ui->jabberID->setReadOnly(true); + m_ui->url->setReadOnly(true); } connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); connect(m_ui->buttonBox, &QDialogButtonBox::rejected, m_ui->buttonBox, &QDialogButtonBox::deleteLater); + + int height = m_ui->personalForm->minimumSize().height(); + avatar.setMaximumSize(avatar.widthForHeight(height), height); + avatar.setMinimumSize(avatar.widthForHeight(height), height); } VCard::~VCard() @@ -54,6 +65,11 @@ VCard::~VCard() void VCard::setVCard(const QString& jid, const Shared::VCard& card) { m_ui->jabberID->setText(jid); + setVCard(card); +} + +void VCard::setVCard(const Shared::VCard& card) +{ m_ui->firstName->setText(card.getFirstName()); m_ui->middleName->setText(card.getMiddleName()); m_ui->lastName->setText(card.getLastName()); @@ -65,17 +81,24 @@ void VCard::setVCard(const QString& jid, const Shared::VCard& card) //m_ui->organizationRole->setText(card.get()); m_ui->description->setText(card.getDescription()); - QString path; switch (card.getAvatarType()) { case Shared::Avatar::empty: - path = QApplication::palette().window().color().lightnessF() > 0.5 ? ":/images/fallback/dark/big/user.svg" : ":/images/fallback/light/big/user.svg"; + avatar.setPath(QApplication::palette().window().color().lightnessF() > 0.5 ? ":/images/fallback/dark/big/user.svg" : ":/images/fallback/light/big/user.svg", 256, 256); break; case Shared::Avatar::autocreated: case Shared::Avatar::valid: - path = card.getAvatarPath(); + avatar.setPath(card.getAvatarPath()); break; } - avatar.setPath(path); + + int height = m_ui->personalForm->minimumSize().height(); + avatar.setMaximumSize(avatar.widthForHeight(height), height); + avatar.setMinimumSize(avatar.widthForHeight(height), height); +} + +QString VCard::getJid() const +{ + return m_ui->jabberID->text(); } void VCard::onButtonBoxAccepted() @@ -88,5 +111,5 @@ void VCard::onButtonBoxAccepted() card.setBirthday(m_ui->birthday->date()); card.setDescription(m_ui->description->toPlainText()); - emit saveVCard(m_ui->jabberID->text(), card); + emit saveVCard(card); } diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard.h index dc14172..f4c81be 100644 --- a/ui/widgets/vcard.h +++ b/ui/widgets/vcard.h @@ -37,13 +37,15 @@ class VCard : public QWidget { Q_OBJECT public: - VCard(bool edit = false, QWidget* parent = nullptr); + VCard(const QString& jid, bool edit = false, QWidget* parent = nullptr); ~VCard(); + void setVCard(const Shared::VCard& card); void setVCard(const QString& jid, const Shared::VCard& card); + QString getJid() const; signals: - void saveVCard(const QString& jid, const Shared::VCard& card); + void saveVCard(const Shared::VCard& card); private slots: void onButtonBoxAccepted(); diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui index 2ebae41..179e235 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard.ui @@ -6,7 +6,7 @@ 0 0 - 544 + 560 534 @@ -80,6 +80,12 @@ + + QFrame::NoFrame + + + QFrame::Plain + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Personal information</span></p></body></html> @@ -97,6 +103,9 @@ + + QLayout::SetDefaultConstraint + Qt::AlignHCenter|Qt::AlignTop @@ -104,13 +113,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -120,13 +129,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -166,13 +175,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -192,13 +201,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -219,35 +228,6 @@ - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - @@ -314,7 +294,7 @@ - Name + Organization name organizationName @@ -325,13 +305,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -340,7 +320,7 @@ - Department + Unit / Department organizationDepartment @@ -351,13 +331,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -366,7 +346,7 @@ - Role + Role / Profession organizationRole @@ -377,13 +357,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -392,7 +372,7 @@ - Title + Job title organizationTitle @@ -403,13 +383,13 @@ - 150 + 200 0 - 300 + 350 16777215 @@ -479,7 +459,7 @@ 0 0 - 532 + 548 410 @@ -547,7 +527,7 @@ - + 150 From 652381b06762b5d1387dcc27efdea2a9ff2604bd Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 23 Oct 2019 17:49:56 +0300 Subject: [PATCH 09/20] changing avatar in local vcard, no uploading yet --- ui/models/roster.cpp | 20 +++--- ui/squawk.cpp | 16 +++++ ui/squawk.h | 2 + ui/utils/image.cpp | 16 ----- ui/utils/image.h | 3 - ui/widgets/conversation.cpp | 6 +- ui/widgets/vcard.cpp | 135 ++++++++++++++++++++++++++++++------ ui/widgets/vcard.h | 23 +++++- ui/widgets/vcard.ui | 63 +++++++++++++++-- 9 files changed, 221 insertions(+), 63 deletions(-) diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 5ea5b2e..33998dc 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -29,17 +29,14 @@ Models::Roster::Roster(QObject* parent): groups(), contacts() { - connect(accountsModel, - SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)), - this, - SLOT(onAccountDataChanged(const QModelIndex&, const QModelIndex&, const QVector&))); - connect(root, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onChildChanged(Models::Item*, int, int))); - connect(root, SIGNAL(childIsAboutToBeInserted(Item*, int, int)), this, SLOT(onChildIsAboutToBeInserted(Item*, int, int))); - connect(root, SIGNAL(childInserted()), this, SLOT(onChildInserted())); - connect(root, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)), this, SLOT(onChildIsAboutToBeRemoved(Item*, int, int))); - connect(root, SIGNAL(childRemoved()), this, SLOT(onChildRemoved())); - connect(root, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SLOT(onChildIsAboutToBeMoved(Item*, int, int, Item*, int))); - connect(root, SIGNAL(childMoved()), this, SLOT(onChildMoved())); + connect(accountsModel, &Accounts::dataChanged, this, &Roster::onAccountDataChanged); + connect(root, &Item::childChanged, this, &Roster::onChildChanged); + connect(root, &Item::childIsAboutToBeInserted, this, &Roster::onChildIsAboutToBeInserted); + connect(root, &Item::childInserted, this, &Roster::onChildInserted); + connect(root, &Item::childIsAboutToBeRemoved, this, &Roster::onChildIsAboutToBeRemoved); + connect(root, &Item::childRemoved, this, &Roster::onChildRemoved); + connect(root, &Item::childIsAboutToBeMoved, this, &Roster::onChildIsAboutToBeMoved); + connect(root, &Item::childMoved, this, &Roster::onChildMoved); } Models::Roster::~Roster() @@ -69,6 +66,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const case Qt::DisplayRole: { if (index.column() != 0) { + result = ""; break; } switch (item->type) { diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 051e691..b5cb84c 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -56,6 +56,8 @@ Squawk::Squawk(QWidget *parent) : connect(rosterModel.accountsModel, SIGNAL(sizeChanged(unsigned int)), this, SLOT(onAccountsSizeChanged(unsigned int))); //m_ui->mainToolBar->addWidget(m_ui->comboBox); + + setWindowTitle(tr("Contact list")); } Squawk::~Squawk() { @@ -768,10 +770,16 @@ void Squawk::onActivateVCard(const QString& account, const QString& jid, bool ed card = itr->second; } else { card = new VCard(jid, edition); + if (edition) { + card->setWindowTitle(tr("%1 account card").arg(account)); + } else { + card->setWindowTitle(tr("%1 contact card").arg(jid)); + } card->setAttribute(Qt::WA_DeleteOnClose); vCards.insert(std::make_pair(jid, card)); connect(card, &VCard::destroyed, this, &Squawk::onVCardClosed); + connect(card, &VCard::saveVCard, std::bind( &Squawk::onVCardSave, this, std::placeholders::_1, account)); } card->show(); @@ -780,3 +788,11 @@ void Squawk::onActivateVCard(const QString& account, const QString& jid, bool ed emit requestVCard(account, jid); } + +void Squawk::onVCardSave(const Shared::VCard& card, const QString& account) +{ + VCard* widget = static_cast(sender()); + emit uploadVCard(account, card); + + widget->deleteLater(); +} diff --git a/ui/squawk.h b/ui/squawk.h index 7308882..bf6582f 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -73,6 +73,7 @@ signals: 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 uploadVCard(const QString& account, const Shared::VCard& card); public slots: void newAccount(const QMap& account); @@ -126,6 +127,7 @@ private slots: void onAccountsClosed(QObject* parent = 0); void onConversationClosed(QObject* parent = 0); void onVCardClosed(); + void onVCardSave(const Shared::VCard& card, const QString& account); void onActivateVCard(const QString& account, const QString& jid, bool edition = false); void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); diff --git a/ui/utils/image.cpp b/ui/utils/image.cpp index dca8153..1d09709 100644 --- a/ui/utils/image.cpp +++ b/ui/utils/image.cpp @@ -29,16 +29,6 @@ Image::Image(const QString& path, quint16 p_minWidth, QWidget* parent): recalculateAspectRatio(); } -Image::Image(const QString& path, quint16 width, quint16 height, quint16 p_minWidth, QWidget* parent): - QLabel(parent), - pixmap(QIcon(path).pixmap(QSize(width, height))), - aspectRatio(0), - minWidth(p_minWidth) -{ - setScaledContents(true); - recalculateAspectRatio(); -} - Image::~Image() { @@ -83,9 +73,3 @@ void Image::setPath(const QString& path) pixmap = QPixmap(path); recalculateAspectRatio(); } - -void Image::setPath(const QString& path, quint16 width, quint16 height) -{ - pixmap = QPixmap(QIcon(path).pixmap(QSize(width, height))); - recalculateAspectRatio(); -} diff --git a/ui/utils/image.h b/ui/utils/image.h index 82071ca..883ddf4 100644 --- a/ui/utils/image.h +++ b/ui/utils/image.h @@ -21,7 +21,6 @@ #include #include -#include /** * @todo write docs @@ -30,7 +29,6 @@ class Image : public QLabel { public: Image(const QString& path, quint16 minWidth = 50, QWidget* parent = nullptr); - Image(const QString& path, quint16 width, quint16 height, quint16 minWidth = 50, QWidget* parent = nullptr); ~Image(); @@ -38,7 +36,6 @@ public: int widthForHeight(int height) const; bool hasHeightForWidth() const override; void setPath(const QString& path); - void setPath(const QString& path, quint16 width, quint16 height); void setMinWidth(quint16 minWidth); private: diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index d661a5c..c281258 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -257,11 +257,11 @@ void Conversation::showEvent(QShowEvent* event) void Conversation::onAttach() { - QFileDialog* d = new QFileDialog(this, "Chose a file to send"); + QFileDialog* d = new QFileDialog(this, tr("Chose a file to send")); d->setFileMode(QFileDialog::ExistingFile); - connect(d, SIGNAL(accepted()), this, SLOT(onFileSelected())); - connect(d, SIGNAL(rejected()), d, SLOT(deleteLater())); + connect(d, &QFileDialog::accepted, this, &Conversation::onFileSelected); + connect(d, &QFileDialog::rejected, d, &QFileDialog::deleteLater); d->show(); } diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index e19538a..552e078 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -19,22 +19,37 @@ #include "vcard.h" #include "ui_vcard.h" +#include + +const std::set VCard::supportedTypes = {"image/jpeg", "image/png"}; + VCard::VCard(const QString& jid, bool edit, QWidget* parent): QWidget(parent), m_ui(new Ui::VCard()), - avatar(QApplication::palette().window().color().lightnessF() > 0.5 ? ":/images/fallback/dark/big/user.svg" : ":/images/fallback/light/big/user.svg", 256, 256, 256) + avatarButtonMargins(), + avatarMenu(nullptr), + editable(edit), + currentAvatarType(Shared::Avatar::empty), + currentAvatarPath("") { m_ui->setupUi(this); m_ui->jabberID->setText(jid); m_ui->jabberID->setReadOnly(true); - QGridLayout* general = static_cast(m_ui->General->layout()); - general->addWidget(&avatar, 2, 2, 1, 1); - avatar.setFrameShape(QFrame::StyledPanel); - avatar.setFrameShadow(QFrame::Sunken); - avatar.setMargin(6); + + QAction* setAvatar = m_ui->actionSetAvatar; + QAction* clearAvatar = m_ui->actionClearAvatar; + + connect(setAvatar, &QAction::triggered, this, &VCard::onSetAvatar); + connect(clearAvatar, &QAction::triggered, this, &VCard::onClearAvatar); + + setAvatar->setEnabled(true); + clearAvatar->setEnabled(false); if (edit) { - + avatarMenu = new QMenu(); + m_ui->avatarButton->setMenu(avatarMenu); + avatarMenu->addAction(setAvatar); + avatarMenu->addAction(clearAvatar); } else { m_ui->buttonBox->hide(); m_ui->firstName->setReadOnly(true); @@ -53,13 +68,17 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); connect(m_ui->buttonBox, &QDialogButtonBox::rejected, m_ui->buttonBox, &QDialogButtonBox::deleteLater); - int height = m_ui->personalForm->minimumSize().height(); - avatar.setMaximumSize(avatar.widthForHeight(height), height); - avatar.setMinimumSize(avatar.widthForHeight(height), height); + avatarButtonMargins = m_ui->avatarButton->size(); + + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + m_ui->avatarButton->setIconSize(QSize(height, height)); } VCard::~VCard() { + if (editable) { + avatarMenu->deleteLater(); + } } void VCard::setVCard(const QString& jid, const Shared::VCard& card) @@ -80,20 +99,10 @@ void VCard::setVCard(const Shared::VCard& card) //m_ui->organizationTitle->setText(card.get()); //m_ui->organizationRole->setText(card.get()); m_ui->description->setText(card.getDescription()); + currentAvatarType = card.getAvatarType(); + currentAvatarPath = card.getAvatarPath(); - switch (card.getAvatarType()) { - case Shared::Avatar::empty: - avatar.setPath(QApplication::palette().window().color().lightnessF() > 0.5 ? ":/images/fallback/dark/big/user.svg" : ":/images/fallback/light/big/user.svg", 256, 256); - break; - case Shared::Avatar::autocreated: - case Shared::Avatar::valid: - avatar.setPath(card.getAvatarPath()); - break; - } - - int height = m_ui->personalForm->minimumSize().height(); - avatar.setMaximumSize(avatar.widthForHeight(height), height); - avatar.setMinimumSize(avatar.widthForHeight(height), height); + updateAvatar(); } QString VCard::getJid() const @@ -110,6 +119,86 @@ void VCard::onButtonBoxAccepted() card.setNickName(m_ui->nickName->text()); card.setBirthday(m_ui->birthday->date()); card.setDescription(m_ui->description->toPlainText()); + card.setAvatarPath(currentAvatarPath); + card.setAvatarType(currentAvatarType); emit saveVCard(card); } + +void VCard::onClearAvatar() +{ + currentAvatarType = Shared::Avatar::empty; + currentAvatarPath = ""; + + updateAvatar(); +} + +void VCard::onSetAvatar() +{ + QFileDialog* d = new QFileDialog(this, tr("Chose your new avatar")); + d->setFileMode(QFileDialog::ExistingFile); + d->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); + + connect(d, &QFileDialog::accepted, this, &VCard::onAvatarSelected); + connect(d, &QFileDialog::rejected, d, &QFileDialog::deleteLater); + + d->show(); +} + +void VCard::updateAvatar() +{ + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + switch (currentAvatarType) { + case Shared::Avatar::empty: + m_ui->avatarButton->setIcon(Shared::icon("user", true)); + m_ui->avatarButton->setIconSize(QSize(height, height)); + m_ui->actionClearAvatar->setEnabled(false); + break; + case Shared::Avatar::autocreated: + case Shared::Avatar::valid: + QPixmap pixmap(currentAvatarPath); + qreal h = pixmap.height(); + qreal w = pixmap.width(); + qreal aspectRatio = w / h; + m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); + m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); + m_ui->actionClearAvatar->setEnabled(true); + break; + } +} + +void VCard::onAvatarSelected() +{ + QFileDialog* d = static_cast(sender()); + QMimeDatabase db; + QString path = d->selectedFiles().front(); + QMimeType type = db.mimeTypeForFile(path); + d->deleteLater(); + + if (supportedTypes.find(type.name()) == supportedTypes.end()) { + qDebug() << "Selected for avatar file is not supported, skipping"; + } else { + QImage src(path); + QImage dst; + if (src.width() > 160 || src.height() > 160) { + dst = src.scaled(160, 160, Qt::KeepAspectRatio); + } + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + m_ui->jabberID->text() + ".temp." + type.preferredSuffix(); + QFile oldTemp(path); + if (oldTemp.exists()) { + if (!oldTemp.remove()) { + qDebug() << "Error removing old temp avatar" << path; + return; + } + } + bool success = dst.save(path); + if (success) { + currentAvatarPath = path; + currentAvatarType = Shared::Avatar::valid; + + updateAvatar(); + } else { + qDebug() << "couldn't save avatar" << path << ", skipping"; + } + } +} diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard.h index f4c81be..afba227 100644 --- a/ui/widgets/vcard.h +++ b/ui/widgets/vcard.h @@ -21,9 +21,16 @@ #include #include +#include +#include +#include +#include +#include +#include + +#include #include "../../global.h" -#include "../utils/image.h" namespace Ui { @@ -49,10 +56,22 @@ signals: private slots: void onButtonBoxAccepted(); + void onClearAvatar(); + void onSetAvatar(); + void onAvatarSelected(); private: QScopedPointer m_ui; - Image avatar; + QSize avatarButtonMargins; + QMenu* avatarMenu; + bool editable; + Shared::Avatar currentAvatarType; + QString currentAvatarPath; + + static const std::set supportedTypes; + +private: + void updateAvatar(); }; #endif // VCARD_H diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui index 179e235..406ca9a 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard.ui @@ -6,8 +6,8 @@ 0 0 - 560 - 534 + 537 + 539 @@ -411,6 +411,43 @@ + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonIconOnly + + + Qt::NoArrow + + + @@ -459,8 +496,8 @@ 0 0 - 548 - 410 + 525 + 415 @@ -625,7 +662,7 @@ - + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> @@ -717,6 +754,22 @@ + + + + + + Set avatar + + + + + + + + Clear avatar + + firstName From 36c71968bc46e0230e688cc897d72bd54a1964fe Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 24 Oct 2019 12:42:38 +0300 Subject: [PATCH 10/20] basic avatar/vcard changes uploads and roster reaction --- core/account.cpp | 88 +++++++++++++++++++++++++++++++++++++++----- core/account.h | 1 + core/squawk.cpp | 10 +++++ core/squawk.h | 1 + global.h | 1 + main.cpp | 1 + ui/models/roster.cpp | 4 +- ui/widgets/vcard.cpp | 2 +- 8 files changed, 97 insertions(+), 11 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 04d2947..a6b0bde 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -103,15 +103,15 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& if (!avatar->exists()) { delete avatar; avatar = new QFile(path + "/avatar.jpg"); - QString type = "jpg"; + type = "jpg"; if (!avatar->exists()) { delete avatar; avatar = new QFile(path + "/avatar.jpeg"); - QString type = "jpeg"; + type = "jpeg"; if (!avatar->exists()) { delete avatar; avatar = new QFile(path + "/avatar.gif"); - QString type = "gif"; + type = "gif"; } } } @@ -1337,7 +1337,6 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) if (confItr == conferences.end()) { if (jid == getLogin() + "@" + getServer()) { onOwnVCardReceived(card); - } else { qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; } @@ -1375,13 +1374,18 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) vCard.setAvatarType(Shared::Avatar::empty); } + QMap cd = { + {"avatarState", static_cast(vCard.getAvatarType())}, + {"avatarPath", vCard.getAvatarPath()} + }; + emit changeContact(jid, cd); emit receivedVCard(jid, vCard); } void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) { QByteArray ava = card.photo(); - bool changed = false; + bool avaChanged = false; QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/"; if (ava.size() > 0) { QCryptographicHash sha1(QCryptographicHash::Sha1); @@ -1407,7 +1411,7 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) newAvatar.close(); avatarHash = newHash; avatarType = newType.preferredSuffix(); - changed = true; + avaChanged = true; } else { qDebug() << "Received new avatar for account" << name << "but can't save it"; if (oldToRemove) { @@ -1425,7 +1429,7 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) newAvatar.close(); avatarHash = newHash; avatarType = newType.preferredSuffix(); - changed = true; + avaChanged = true; } else { qDebug() << "Received new avatar for account" << name << "but can't save it"; } @@ -1436,12 +1440,14 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) if (!oldAvatar.remove()) { qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing"; } else { - changed = true; + avatarType = ""; + avatarHash = ""; + avaChanged = true; } } } - if (changed) { + if (avaChanged) { QMap change; if (avatarType.size() > 0) { presence.setPhotoHash(avatarHash.toUtf8()); @@ -1453,6 +1459,7 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) change.insert("avatarPath", ""); } client.setClientPresence(presence); + emit changed(change); } ownVCardRequestInProgress = false; @@ -1504,3 +1511,66 @@ void Core::Account::requestVCard(const QString& jid) } } } + +void Core::Account::uploadVCard(const Shared::VCard& card) +{ + QXmppVCardIq iq; + iq.setFirstName(card.getFirstName()); + iq.setMiddleName(card.getMiddleName()); + iq.setLastName(card.getLastName()); + iq.setNickName(card.getNickName()); + iq.setBirthday(card.getBirthday()); + iq.setDescription(card.getDescription()); + + bool avatarChanged = false; + if (card.getAvatarType() == Shared::Avatar::empty) { + if (avatarType.size() > 0) { + avatarChanged = true; + } + } else { + QString newPath = card.getAvatarPath(); + QString oldPath = getAvatarPath(); + QByteArray data; + QString type; + if (newPath != oldPath) { + QFile avatar(newPath); + if (!avatar.open(QFile::ReadOnly)) { + qDebug() << "An attempt to upload new vCard to account" << name + << "but it wasn't possible to read file" << newPath + << "which was supposed to be new avatar, uploading old avatar"; + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; + avatarChanged = true; + } else { + data = oA.readAll(); + } + } + } else { + data = avatar.readAll(); + avatarChanged = true; + } + } else { + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; + avatarChanged = true; + } else { + data = oA.readAll(); + } + } + } + + if (data.size() > 0) { + QMimeDatabase db; + type = db.mimeTypeForData(data).name(); + iq.setPhoto(data); + iq.setPhotoType(type); + } + } + + client.vCardManager().setClientVCard(iq); + onOwnVCardReceived(iq); +} diff --git a/core/account.h b/core/account.h index b5b8c46..371a561 100644 --- a/core/account.h +++ b/core/account.h @@ -88,6 +88,7 @@ public: void removeRoomRequest(const QString& jid); void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); void requestVCard(const QString& jid); + void uploadVCard(const Shared::VCard& card); signals: void changed(const QMap& data); diff --git a/core/squawk.cpp b/core/squawk.cpp index 35977a4..9f421c9 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -543,3 +543,13 @@ void Core::Squawk::requestVCard(const QString& account, const QString& jid) } itr->second->requestVCard(jid); } + +void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card) +{ + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping"; + return; + } + itr->second->uploadVCard(card); +} diff --git a/core/squawk.h b/core/squawk.h index 9176f28..88ea860 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -93,6 +93,7 @@ public slots: 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 uploadVCard(const QString& account, const Shared::VCard& card); private: typedef std::deque Accounts; diff --git a/global.h b/global.h index b20e616..0daa20b 100644 --- a/global.h +++ b/global.h @@ -437,6 +437,7 @@ static const std::map> icons = { {"view-refresh", {"view-refresh", "view-refresh"}}, {"send", {"document-send", "send"}}, {"clean", {"edit-clear-all", "clean"}}, + {"user", {"user", "user"}}, }; }; diff --git a/main.cpp b/main.cpp index 49a9875..1c455bc 100644 --- a/main.cpp +++ b/main.cpp @@ -106,6 +106,7 @@ int main(int argc, char *argv[]) QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest); QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest); QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard); + QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard); QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount); QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact); diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 33998dc..6e49104 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -98,6 +98,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const result = acc->getStatusIcon(false); } else if (col == 1) { QString path = acc->getAvatarPath(); + if (path.size() > 0) { result = QIcon(path); } @@ -641,7 +642,8 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c void Models::Roster::onChildChanged(Models::Item* item, int row, int col) { QModelIndex index = createIndex(row, 0, item); - emit dataChanged(index, index); + QModelIndex index2 = createIndex(row, 1, item); + emit dataChanged(index, index2); } void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last) diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index 552e078..1da6c2e 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -66,7 +66,7 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): } connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); - connect(m_ui->buttonBox, &QDialogButtonBox::rejected, m_ui->buttonBox, &QDialogButtonBox::deleteLater); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &VCard::close); avatarButtonMargins = m_ui->avatarButton->size(); From 566fc1f2fb5ee25cba97288c191c71430bf4848c Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 25 Oct 2019 16:38:48 +0300 Subject: [PATCH 11/20] new fallback icons, new fields in vCard, fix about recreation new avatar on each request --- core/account.cpp | 29 +- global.cpp | 84 ++++ global.h | 22 + .../images/fallback/dark/big/edit-rename.svg | 14 + .../images/fallback/dark/big/group-new.svg | 14 + resources/images/fallback/dark/big/group.svg | 14 + .../fallback/dark/big/user-properties.svg | 14 + .../fallback/dark/small/edit-rename.svg | 13 + .../images/fallback/dark/small/group-new.svg | 13 + .../images/fallback/dark/small/group.svg | 13 + .../fallback/dark/small/user-properties.svg | 13 + .../images/fallback/light/big/edit-rename.svg | 14 + .../images/fallback/light/big/group-new.svg | 14 + resources/images/fallback/light/big/group.svg | 14 + .../fallback/light/big/user-properties.svg | 14 + .../fallback/light/small/edit-rename.svg | 13 + .../images/fallback/light/small/group-new.svg | 13 + .../images/fallback/light/small/group.svg | 13 + .../fallback/light/small/user-properties.svg | 13 + resources/resources.qrc | 16 + ui/models/roster.cpp | 20 + ui/models/roster.h | 1 + ui/squawk.cpp | 11 +- ui/widgets/vcard.cpp | 17 +- ui/widgets/vcard.ui | 418 ++++++++++-------- 25 files changed, 629 insertions(+), 205 deletions(-) create mode 100644 resources/images/fallback/dark/big/edit-rename.svg create mode 100644 resources/images/fallback/dark/big/group-new.svg create mode 100644 resources/images/fallback/dark/big/group.svg create mode 100644 resources/images/fallback/dark/big/user-properties.svg create mode 100644 resources/images/fallback/dark/small/edit-rename.svg create mode 100644 resources/images/fallback/dark/small/group-new.svg create mode 100644 resources/images/fallback/dark/small/group.svg create mode 100644 resources/images/fallback/dark/small/user-properties.svg create mode 100644 resources/images/fallback/light/big/edit-rename.svg create mode 100644 resources/images/fallback/light/big/group-new.svg create mode 100644 resources/images/fallback/light/big/group.svg create mode 100644 resources/images/fallback/light/big/user-properties.svg create mode 100644 resources/images/fallback/light/small/edit-rename.svg create mode 100644 resources/images/fallback/light/small/group-new.svg create mode 100644 resources/images/fallback/light/small/group.svg create mode 100644 resources/images/fallback/light/small/user-properties.svg diff --git a/core/account.cpp b/core/account.cpp index a6b0bde..f3e4156 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -1353,18 +1353,28 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) if (ava.size() > 0) { item->setAvatar(ava); } else { - item->setAutoGeneratedAvatar(); + if (!item->hasAvatar() || !item->isAvatarAutoGenerated()) { + item->setAutoGeneratedAvatar(); + } } Shared::VCard vCard; + vCard.setFullName(card.fullName()); vCard.setFirstName(card.firstName()); vCard.setMiddleName(card.middleName()); vCard.setLastName(card.lastName()); vCard.setBirthday(card.birthday()); vCard.setNickName(card.nickName()); vCard.setDescription(card.description()); + vCard.setUrl(card.url()); + QXmppVCardOrganization org = card.organization(); + vCard.setOrgName(org.organization()); + vCard.setOrgRole(org.role()); + vCard.setOrgUnit(org.unit()); + vCard.setOrgTitle(org.title()); + if (item->hasAvatar()) { - if (item->isAvatarAutoGenerated()) { + if (!item->isAvatarAutoGenerated()) { vCard.setAvatarType(Shared::Avatar::valid); } else { vCard.setAvatarType(Shared::Avatar::autocreated); @@ -1465,12 +1475,19 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) ownVCardRequestInProgress = false; Shared::VCard vCard; + vCard.setFullName(card.fullName()); vCard.setFirstName(card.firstName()); vCard.setMiddleName(card.middleName()); vCard.setLastName(card.lastName()); vCard.setBirthday(card.birthday()); vCard.setNickName(card.nickName()); vCard.setDescription(card.description()); + vCard.setUrl(card.url()); + QXmppVCardOrganization org = card.organization(); + vCard.setOrgName(org.organization()); + vCard.setOrgRole(org.role()); + vCard.setOrgUnit(org.unit()); + vCard.setOrgTitle(org.title()); if (avatarType.size() > 0) { vCard.setAvatarType(Shared::Avatar::valid); vCard.setAvatarPath(path + "avatar." + avatarType); @@ -1515,12 +1532,20 @@ void Core::Account::requestVCard(const QString& jid) void Core::Account::uploadVCard(const Shared::VCard& card) { QXmppVCardIq iq; + iq.setFullName(card.getFullName()); iq.setFirstName(card.getFirstName()); iq.setMiddleName(card.getMiddleName()); iq.setLastName(card.getLastName()); iq.setNickName(card.getNickName()); iq.setBirthday(card.getBirthday()); iq.setDescription(card.getDescription()); + iq.setUrl(card.getUrl()); + QXmppVCardOrganization org; + org.setOrganization(card.getOrgName()); + org.setUnit(card.getOrgUnit()); + org.setRole(card.getOrgRole()); + org.setTitle(card.getOrgTitle()); + iq.setOrganization(org); bool avatarChanged = false; if (card.getAvatarType() == Shared::Avatar::empty) { diff --git a/global.cpp b/global.cpp index fa63539..efadd6c 100644 --- a/global.cpp +++ b/global.cpp @@ -323,11 +323,17 @@ Shared::VCard::Address::Address(const QString& zCode, const QString& cntry, cons {} Shared::VCard::VCard(): + fullName(), firstName(), middleName(), lastName(), nickName(), description(), + url(), + organizationName(), + organizationUnit(), + organizationRole(), + jobTitle(), birthday(), photoType(Avatar::empty), photoPath(), @@ -338,11 +344,17 @@ Shared::VCard::VCard(): {} Shared::VCard::VCard(const QDateTime& creationTime): + fullName(), firstName(), middleName(), lastName(), nickName(), description(), + url(), + organizationName(), + organizationUnit(), + organizationRole(), + jobTitle(), birthday(), photoType(Avatar::empty), photoPath(), @@ -449,6 +461,78 @@ void Shared::VCard::setNickName(const QString& nick) } } +QString Shared::VCard::getFullName() const +{ + return fullName; +} + +QString Shared::VCard::getUrl() const +{ + return url; +} + +void Shared::VCard::setFullName(const QString& name) +{ + if (fullName != name) { + fullName = name; + } +} + +void Shared::VCard::setUrl(const QString& u) +{ + if (url != u) { + url = u; + } +} + +QString Shared::VCard::getOrgName() const +{ + return organizationName; +} + +QString Shared::VCard::getOrgRole() const +{ + return organizationRole; +} + +QString Shared::VCard::getOrgTitle() const +{ + return jobTitle; +} + +QString Shared::VCard::getOrgUnit() const +{ + return organizationUnit; +} + +void Shared::VCard::setOrgName(const QString& name) +{ + if (organizationName != name) { + organizationName = name; + } +} + +void Shared::VCard::setOrgRole(const QString& role) +{ + if (organizationRole != role) { + organizationRole = role; + } +} + +void Shared::VCard::setOrgTitle(const QString& title) +{ + if (jobTitle != title) { + jobTitle = title; + } +} + +void Shared::VCard::setOrgUnit(const QString& unit) +{ + if (organizationUnit != unit) { + organizationUnit = unit; + } +} + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index 0daa20b..1261d1f 100644 --- a/global.h +++ b/global.h @@ -272,6 +272,8 @@ public: VCard(); VCard(const QDateTime& creationTime); + QString getFullName() const; + void setFullName(const QString& name); QString getFirstName() const; void setFirstName(const QString& first); QString getMiddleName() const; @@ -282,19 +284,35 @@ public: void setNickName(const QString& nick); QString getDescription() const; void setDescription(const QString& descr); + QString getUrl() const; + void setUrl(const QString& u); QDate getBirthday() const; void setBirthday(const QDate& date); Avatar getAvatarType() const; void setAvatarType(Avatar type); QString getAvatarPath() const; void setAvatarPath(const QString& path); + QString getOrgName() const; + void setOrgName(const QString& name); + QString getOrgUnit() const; + void setOrgUnit(const QString& unit); + QString getOrgRole() const; + void setOrgRole(const QString& role); + QString getOrgTitle() const; + void setOrgTitle(const QString& title); private: + QString fullName; QString firstName; QString middleName; QString lastName; QString nickName; QString description; + QString url; + QString organizationName; + QString organizationUnit; + QString organizationRole; + QString jobTitle; QDate birthday; Avatar photoType; QString photoPath; @@ -438,6 +456,10 @@ static const std::map> icons = { {"send", {"document-send", "send"}}, {"clean", {"edit-clear-all", "clean"}}, {"user", {"user", "user"}}, + {"user-properties", {"user-properties", "user-properties"}}, + {"edit-rename", {"edit-rename", "edit-rename"}}, + {"group", {"group", "group"}}, + {"group-new", {"resurce-group-new", "group-new"}}, }; }; diff --git a/resources/images/fallback/dark/big/edit-rename.svg b/resources/images/fallback/dark/big/edit-rename.svg new file mode 100644 index 0000000..8075a3b --- /dev/null +++ b/resources/images/fallback/dark/big/edit-rename.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/group-new.svg b/resources/images/fallback/dark/big/group-new.svg new file mode 100644 index 0000000..a28270a --- /dev/null +++ b/resources/images/fallback/dark/big/group-new.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/group.svg b/resources/images/fallback/dark/big/group.svg new file mode 100644 index 0000000..0b9c379 --- /dev/null +++ b/resources/images/fallback/dark/big/group.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/user-properties.svg b/resources/images/fallback/dark/big/user-properties.svg new file mode 100644 index 0000000..afec990 --- /dev/null +++ b/resources/images/fallback/dark/big/user-properties.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/edit-rename.svg b/resources/images/fallback/dark/small/edit-rename.svg new file mode 100644 index 0000000..18ccc58 --- /dev/null +++ b/resources/images/fallback/dark/small/edit-rename.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/group-new.svg b/resources/images/fallback/dark/small/group-new.svg new file mode 100644 index 0000000..848af85 --- /dev/null +++ b/resources/images/fallback/dark/small/group-new.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/group.svg b/resources/images/fallback/dark/small/group.svg new file mode 100644 index 0000000..7ca4c26 --- /dev/null +++ b/resources/images/fallback/dark/small/group.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/user-properties.svg b/resources/images/fallback/dark/small/user-properties.svg new file mode 100644 index 0000000..fa4c9e0 --- /dev/null +++ b/resources/images/fallback/dark/small/user-properties.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/big/edit-rename.svg b/resources/images/fallback/light/big/edit-rename.svg new file mode 100644 index 0000000..0c3d22c --- /dev/null +++ b/resources/images/fallback/light/big/edit-rename.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/group-new.svg b/resources/images/fallback/light/big/group-new.svg new file mode 100644 index 0000000..9c8b823 --- /dev/null +++ b/resources/images/fallback/light/big/group-new.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/group.svg b/resources/images/fallback/light/big/group.svg new file mode 100644 index 0000000..ef4758b --- /dev/null +++ b/resources/images/fallback/light/big/group.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/user-properties.svg b/resources/images/fallback/light/big/user-properties.svg new file mode 100644 index 0000000..9a14b93 --- /dev/null +++ b/resources/images/fallback/light/big/user-properties.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/small/edit-rename.svg b/resources/images/fallback/light/small/edit-rename.svg new file mode 100644 index 0000000..6a84496 --- /dev/null +++ b/resources/images/fallback/light/small/edit-rename.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/group-new.svg b/resources/images/fallback/light/small/group-new.svg new file mode 100644 index 0000000..43b465a --- /dev/null +++ b/resources/images/fallback/light/small/group-new.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/group.svg b/resources/images/fallback/light/small/group.svg new file mode 100644 index 0000000..dfefc94 --- /dev/null +++ b/resources/images/fallback/light/small/group.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/user-properties.svg b/resources/images/fallback/light/small/user-properties.svg new file mode 100644 index 0000000..2a0bebd --- /dev/null +++ b/resources/images/fallback/light/small/user-properties.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/resources.qrc b/resources/resources.qrc index 244db04..3cfaa84 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -32,6 +32,10 @@ images/fallback/dark/big/clean.svg images/fallback/dark/big/send.svg images/fallback/dark/big/mail-attachment.svg + images/fallback/dark/big/group.svg + images/fallback/dark/big/group-new.svg + images/fallback/dark/big/edit-rename.svg + images/fallback/dark/big/user-properties.svg images/fallback/dark/small/absent.svg @@ -64,6 +68,10 @@ images/fallback/dark/small/clean.svg images/fallback/dark/small/send.svg images/fallback/dark/small/mail-attachment.svg + images/fallback/dark/small/group.svg + images/fallback/dark/small/group-new.svg + images/fallback/dark/small/edit-rename.svg + images/fallback/dark/small/user-properties.svg images/fallback/light/big/absent.svg @@ -96,6 +104,10 @@ images/fallback/light/big/clean.svg images/fallback/light/big/send.svg images/fallback/light/big/mail-attachment.svg + images/fallback/light/big/group.svg + images/fallback/light/big/group-new.svg + images/fallback/light/big/edit-rename.svg + images/fallback/light/big/user-properties.svg images/fallback/light/small/absent.svg @@ -128,5 +140,9 @@ images/fallback/light/small/clean.svg images/fallback/light/small/send.svg images/fallback/light/small/mail-attachment.svg + images/fallback/light/small/group.svg + images/fallback/light/small/group-new.svg + images/fallback/light/small/edit-rename.svg + images/fallback/light/small/user-properties.svg diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 6e49104..e124db7 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -934,3 +934,23 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou return gr->hasContact(contact); } } + +QString Models::Roster::getContactIconPath(const QString& account, const QString& jid) +{ + ElId id(account, jid); + std::multimap::const_iterator cItr = contacts.find(id); + QString path = ""; + if (cItr == contacts.end()) { + std::map::const_iterator rItr = rooms.find(id); + if (rItr == rooms.end()) { + qDebug() << "An attempt to get an icon path of non existing contact" << account << ":" << jid << ", returning empty value"; + } else { + //path = rItr->second->getRoomName(); + } + } else { + if (cItr->second->getAvatarState() != Shared::Avatar::empty) { + path = cItr->second->getAvatarPath(); + } + } + return path; +} diff --git a/ui/models/roster.h b/ui/models/roster.h index 30fb884..40c978d 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -73,6 +73,7 @@ public: std::deque groupList(const QString& account) const; bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const; + QString getContactIconPath(const QString& account, const QString& jid); Accounts* accountsModel; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index b5cb84c..b228ac8 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -464,11 +464,16 @@ void Squawk::accountMessage(const QString& account, const Shared::Message& data) void Squawk::notify(const QString& account, const Shared::Message& msg) { - QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid()));; + QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid())); + QString path = QString(rosterModel.getContactIconPath(account, msg.getPenPalJid())); QVariantList args; args << QString(QCoreApplication::applicationName()); args << QVariant(QVariant::UInt); //TODO some normal id - args << QString("mail-message"); //TODO icon + if (path.size() > 0) { + args << path; + } else { + args << QString("mail-message"); + } if (msg.getType() == Shared::Message::groupChat) { args << msg.getFromResource() + " from " + name; } else { @@ -635,7 +640,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) } }); } - QAction* newGroup = groupsMenu->addAction(Shared::icon("resource-group-new"), tr("New group")); + QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group")); newGroup->setEnabled(active); connect(newGroup, &QAction::triggered, [this, accName, cntJID]() { QInputDialog* dialog = new QInputDialog(this); diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index 1da6c2e..c20b4bf 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -52,6 +52,7 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): avatarMenu->addAction(clearAvatar); } else { m_ui->buttonBox->hide(); + m_ui->fullName->setReadOnly(true); m_ui->firstName->setReadOnly(true); m_ui->middleName->setReadOnly(true); m_ui->lastName->setReadOnly(true); @@ -89,16 +90,18 @@ void VCard::setVCard(const QString& jid, const Shared::VCard& card) void VCard::setVCard(const Shared::VCard& card) { + m_ui->fullName->setText(card.getFullName()); m_ui->firstName->setText(card.getFirstName()); m_ui->middleName->setText(card.getMiddleName()); m_ui->lastName->setText(card.getLastName()); m_ui->nickName->setText(card.getNickName()); m_ui->birthday->setDate(card.getBirthday()); - //m_ui->organizationName->setText(card.get()); - //m_ui->organizationDepartment->setText(card.get()); - //m_ui->organizationTitle->setText(card.get()); - //m_ui->organizationRole->setText(card.get()); + m_ui->organizationName->setText(card.getOrgName()); + m_ui->organizationDepartment->setText(card.getOrgUnit()); + m_ui->organizationTitle->setText(card.getOrgTitle()); + m_ui->organizationRole->setText(card.getOrgRole()); m_ui->description->setText(card.getDescription()); + m_ui->url->setText(card.getUrl()); currentAvatarType = card.getAvatarType(); currentAvatarPath = card.getAvatarPath(); @@ -113,12 +116,18 @@ QString VCard::getJid() const void VCard::onButtonBoxAccepted() { Shared::VCard card; + card.setFullName(m_ui->fullName->text()); card.setFirstName(m_ui->firstName->text()); card.setMiddleName(m_ui->middleName->text()); card.setLastName(m_ui->lastName->text()); card.setNickName(m_ui->nickName->text()); card.setBirthday(m_ui->birthday->date()); card.setDescription(m_ui->description->toPlainText()); + card.setUrl(m_ui->url->text()); + card.setOrgName(m_ui->organizationName->text()); + card.setOrgUnit(m_ui->organizationDepartment->text()); + card.setOrgRole(m_ui->organizationRole->text()); + card.setOrgTitle(m_ui->organizationTitle->text()); card.setAvatarPath(currentAvatarPath); card.setAvatarType(currentAvatarType); diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui index 406ca9a..9eabf0b 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard.ui @@ -6,8 +6,8 @@ 0 0 - 537 - 539 + 594 + 595 @@ -94,14 +94,177 @@ - + Qt::Horizontal - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">General</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Organization</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Organization name + + + organizationName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Unit / Department + + + organizationDepartment + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Role / Profession + + + organizationRole + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Job title + + + organizationTitle + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + QLayout::SetDefaultConstraint @@ -228,190 +391,7 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Organization</span></p></body></html> - - - Qt::AlignCenter - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - Organization name - - - organizationName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Unit / Department - - - organizationDepartment - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Role / Profession - - - organizationRole - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Job title - - - organizationTitle - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - - Qt::Horizontal - - - - - - - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">General</span></p></body></html> - - - - + @@ -429,7 +409,8 @@ - + + .. @@ -448,6 +429,43 @@ + + + + Qt::Horizontal + + + + + + + + + + + + Full name + + + fullName + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -496,8 +514,8 @@ 0 0 - 525 - 415 + 582 + 471 @@ -584,6 +602,9 @@ Web site + + url + @@ -756,7 +777,8 @@ - + + .. Set avatar @@ -764,7 +786,8 @@ - + + .. Clear avatar @@ -772,19 +795,22 @@ + fullName firstName middleName lastName nickName birthday + avatarButton organizationName organizationDepartment organizationRole organizationTitle - scrollArea - jabberID - description tabWidget + jabberID + url + description + scrollArea From f3515f1104285a5f551b5f1357d573187da8ae38 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 30 Oct 2019 16:47:21 +0300 Subject: [PATCH 12/20] refactored progress --- ui/CMakeLists.txt | 1 + ui/utils/messageline.cpp | 41 +++----------------- ui/utils/messageline.h | 14 +------ ui/utils/progress.cpp | 84 ++++++++++++++++++++++++++++++++++++++++ ui/utils/progress.h | 57 +++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 48 deletions(-) create mode 100644 ui/utils/progress.cpp create mode 100644 ui/utils/progress.h diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 0f6680a..59a0a4f 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -36,6 +36,7 @@ set(squawkUI_SRC utils/image.cpp utils/flowlayout.cpp utils/badge.cpp + utils/progress.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index 0560344..57894e8 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -31,35 +31,11 @@ MessageLine::MessageLine(bool p_room, QWidget* parent): palNames(), views(), room(p_room), - busyPixmap(new QGraphicsPixmapItem(Shared::icon("view-refresh", true).pixmap(70))), - busyScene(), - busyLabel(&busyScene), - busyLayout(), busyShown(false), - rotation() + progress() { setBackgroundRole(QPalette::Base); layout->addStretch(); - - busyScene.addItem(busyPixmap); - busyLayout.addStretch(); - busyLayout.addWidget(&busyLabel); - busyLayout.addStretch(); - busyLabel.setMaximumSize(70, 70); - busyLabel.setMinimumSize(70, 70); - busyLabel.setSceneRect(0, 0, 70, 70); - busyLabel.setFrameStyle(0); - busyLabel.setContentsMargins(0, 0, 0, 0); - busyLabel.setInteractive(false); - busyPixmap->setTransformOriginPoint(35, 35); - busyPixmap->setTransformationMode(Qt::SmoothTransformation); - busyPixmap->setOffset(0, 0);; - - rotation.setDuration(500); - rotation.setStartValue(0.0f); - rotation.setEndValue(180.0f); - rotation.setLoopCount(-1); - connect(&rotation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(onAnimationValueChanged(const QVariant&))); } MessageLine::~MessageLine() @@ -201,28 +177,21 @@ QString MessageLine::firstMessageId() const void MessageLine::showBusyIndicator() { if (!busyShown) { - layout->insertLayout(0, &busyLayout); + layout->insertWidget(0, &progress); + progress.start(); busyShown = true; - rotation.start(); - busyLabel.show(); } } void MessageLine::hideBusyIndicator() { if (busyShown) { - busyLabel.hide(); - rotation.stop(); - layout->removeItem(&busyLayout); + progress.stop(); + layout->removeWidget(&progress); busyShown = false; } } -void MessageLine::onAnimationValueChanged(const QVariant& value) -{ - busyPixmap->setRotation(value.toReal()); -} - void MessageLine::responseDownloadProgress(const QString& messageId, qreal progress) { Index::const_iterator itr = messageIndex.find(messageId); diff --git a/ui/utils/messageline.h b/ui/utils/messageline.h index 3d7fb56..67280e4 100644 --- a/ui/utils/messageline.h +++ b/ui/utils/messageline.h @@ -25,13 +25,10 @@ #include #include #include -#include -#include -#include -#include #include "../../global.h" #include "message.h" +#include "progress.h" class MessageLine : public QWidget { @@ -85,15 +82,8 @@ private: std::map palNames; std::deque views; bool room; - QGraphicsPixmapItem* busyPixmap; - QGraphicsScene busyScene; - QGraphicsView busyLabel; - QHBoxLayout busyLayout; bool busyShown; - QVariantAnimation rotation; - -private slots: - void onAnimationValueChanged(const QVariant& value); + Progress progress; }; #endif // MESSAGELINE_H diff --git a/ui/utils/progress.cpp b/ui/utils/progress.cpp new file mode 100644 index 0000000..9886270 --- /dev/null +++ b/ui/utils/progress.cpp @@ -0,0 +1,84 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "progress.h" + +Progress::Progress(quint16 p_size, QWidget* parent): + QWidget(parent), + pixmap(new QGraphicsPixmapItem(Shared::icon("view-refresh", true).pixmap(p_size))), + scene(), + label(&scene), + progress(false), + animation(), + size(p_size) +{ + scene.addItem(pixmap); + label.setMaximumSize(size, size); + label.setMinimumSize(size, size); + label.setSceneRect(0, 0, size, size); + label.setFrameStyle(0); + label.setContentsMargins(0, 0, 0, 0); + label.setInteractive(false); + pixmap->setTransformOriginPoint(size / 2, size / 2); + pixmap->setTransformationMode(Qt::SmoothTransformation); + pixmap->setOffset(0, 0);; + + animation.setDuration(500); + animation.setStartValue(0.0f); + animation.setEndValue(180.0f); + animation.setLoopCount(-1); + connect(&animation, &QVariantAnimation::valueChanged, this, &Progress::onValueChanged); + + QGridLayout* layout = new QGridLayout(); + setLayout(layout); + layout->setMargin(0); + layout->setVerticalSpacing(0); + layout->setHorizontalSpacing(0); + + setContentsMargins(0, 0, 0, 0); + + layout->addWidget(&label, 0, 0, 1, 1); + label.hide(); +} + +Progress::~Progress() +{ +} + +void Progress::onValueChanged(const QVariant& value) +{ + pixmap->setRotation(value.toReal()); +} + +void Progress::start() +{ + if (!progress) { + label.show(); + animation.start(); + progress = true; + } +} + +void Progress::stop() +{ + if (progress) { + label.hide(); + animation.stop(); + progress = false; + } +} diff --git a/ui/utils/progress.h b/ui/utils/progress.h new file mode 100644 index 0000000..c6501aa --- /dev/null +++ b/ui/utils/progress.h @@ -0,0 +1,57 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef PROGRESS_H +#define PROGRESS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "../../global.h" + +/** + * @todo write docs + */ +class Progress : public QWidget +{ + Q_OBJECT +public: + Progress(quint16 p_size = 70, QWidget* parent = nullptr); + ~Progress(); + + void start(); + void stop(); + +private slots: + void onValueChanged(const QVariant& value); + +private: + QGraphicsPixmapItem* pixmap; + QGraphicsScene scene; + QGraphicsView label; + bool progress; + QVariantAnimation animation; + quint16 size; +}; + +#endif // PROGRESS_H From e924715a57b0ea3b075560baa5adff84d3c2e938 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 31 Oct 2019 17:01:48 +0300 Subject: [PATCH 13/20] progress for VCard widget, date of receiving --- global.cpp | 5 + global.h | 1 + ui/squawk.cpp | 2 + ui/utils/message.cpp | 1 - ui/utils/progress.cpp | 3 +- ui/widgets/vcard.cpp | 40 +- ui/widgets/vcard.h | 9 + ui/widgets/vcard.ui | 1501 +++++++++++++++++++++-------------------- 8 files changed, 832 insertions(+), 730 deletions(-) diff --git a/global.cpp b/global.cpp index efadd6c..9beb7a1 100644 --- a/global.cpp +++ b/global.cpp @@ -533,6 +533,11 @@ void Shared::VCard::setOrgUnit(const QString& unit) } } +QDateTime Shared::VCard::getReceivingTime() const +{ + return receivingTime; +} + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index 1261d1f..b046efa 100644 --- a/global.h +++ b/global.h @@ -300,6 +300,7 @@ public: void setOrgRole(const QString& role); QString getOrgTitle() const; void setOrgTitle(const QString& title); + QDateTime getReceivingTime() const; private: QString fullName; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index b228ac8..5ddd525 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -751,6 +751,7 @@ void Squawk::responseVCard(const QString& jid, const Shared::VCard& card) std::map::const_iterator itr = vCards.find(jid); if (itr != vCards.end()) { itr->second->setVCard(card); + itr->second->hideProgress(); } } @@ -790,6 +791,7 @@ void Squawk::onActivateVCard(const QString& account, const QString& jid, bool ed card->show(); card->raise(); card->activateWindow(); + card->showProgress(tr("Downloading vCard")); emit requestVCard(account, jid); } diff --git a/ui/utils/message.cpp b/ui/utils/message.cpp index 4c8debe..2498d84 100644 --- a/ui/utils/message.cpp +++ b/ui/utils/message.cpp @@ -63,7 +63,6 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_ dFont.setItalic(true); dFont.setPointSize(dFont.pointSize() - 2); date->setFont(dFont); - date->setForegroundRole(QPalette::ToolTipText); QFont f; f.setBold(true); diff --git a/ui/utils/progress.cpp b/ui/utils/progress.cpp index 9886270..95eafa2 100644 --- a/ui/utils/progress.cpp +++ b/ui/utils/progress.cpp @@ -34,9 +34,10 @@ Progress::Progress(quint16 p_size, QWidget* parent): label.setFrameStyle(0); label.setContentsMargins(0, 0, 0, 0); label.setInteractive(false); + label.setStyleSheet("background: transparent"); pixmap->setTransformOriginPoint(size / 2, size / 2); pixmap->setTransformationMode(Qt::SmoothTransformation); - pixmap->setOffset(0, 0);; + pixmap->setOffset(0, 0); animation.setDuration(500); animation.setStartValue(0.0f); diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index c20b4bf..4584638 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -30,7 +30,10 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): avatarMenu(nullptr), editable(edit), currentAvatarType(Shared::Avatar::empty), - currentAvatarPath("") + currentAvatarPath(""), + progress(new Progress(100)), + progressLabel(new QLabel()), + overlay(new QWidget()) { m_ui->setupUi(this); m_ui->jabberID->setText(jid); @@ -50,6 +53,7 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->avatarButton->setMenu(avatarMenu); avatarMenu->addAction(setAvatar); avatarMenu->addAction(clearAvatar); + m_ui->title->setText(tr("Your card")); } else { m_ui->buttonBox->hide(); m_ui->fullName->setReadOnly(true); @@ -64,6 +68,7 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->organizationRole->setReadOnly(true); m_ui->description->setReadOnly(true); m_ui->url->setReadOnly(true); + m_ui->title->setText(tr("Contact %1 card").arg(jid)); } connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); @@ -73,6 +78,23 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); m_ui->avatarButton->setIconSize(QSize(height, height)); + + QGridLayout* gr = static_cast(layout()); + gr->addWidget(overlay, 0, 0, 4, 1); + QVBoxLayout* nl = new QVBoxLayout(); + QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); + opacity->setOpacity(0.8); + overlay->setLayout(nl); + overlay->setBackgroundRole(QPalette::Base); + overlay->setAutoFillBackground(true); + overlay->setGraphicsEffect(opacity); + progressLabel->setAlignment(Qt::AlignCenter); + progressLabel->setStyleSheet("font: 16pt"); + nl->addStretch(); + nl->addWidget(progress); + nl->addWidget(progressLabel); + nl->addStretch(); + overlay->hide(); } VCard::~VCard() @@ -102,6 +124,9 @@ void VCard::setVCard(const Shared::VCard& card) m_ui->organizationRole->setText(card.getOrgRole()); m_ui->description->setText(card.getDescription()); m_ui->url->setText(card.getUrl()); + + QDateTime receivingTime = card.getReceivingTime(); + m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString())); currentAvatarType = card.getAvatarType(); currentAvatarPath = card.getAvatarPath(); @@ -211,3 +236,16 @@ void VCard::onAvatarSelected() } } } + +void VCard::showProgress(const QString& line) +{ + progressLabel->setText(line); + overlay->show(); + progress->start(); +} + +void VCard::hideProgress() +{ + overlay->hide(); + progress->stop(); +} diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard.h index afba227..4831734 100644 --- a/ui/widgets/vcard.h +++ b/ui/widgets/vcard.h @@ -27,10 +27,14 @@ #include #include #include +#include +#include +#include #include #include "../../global.h" +#include "../utils/progress.h" namespace Ui { @@ -50,6 +54,8 @@ public: void setVCard(const Shared::VCard& card); void setVCard(const QString& jid, const Shared::VCard& card); QString getJid() const; + void showProgress(const QString& = ""); + void hideProgress(); signals: void saveVCard(const Shared::VCard& card); @@ -67,6 +73,9 @@ private: bool editable; Shared::Avatar currentAvatarType; QString currentAvatarPath; + Progress* progress; + QLabel* progressLabel; + QWidget* overlay; static const std::set supportedTypes; diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui index 9eabf0b..a582d3c 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard.ui @@ -2,777 +2,825 @@ VCard + + true + 0 0 594 - 595 + 651 - - - 6 - + - 6 + 0 - 6 + 0 - 6 + 0 - 6 + 0 - - - - Qt::TabFocus + + 0 + + + + + 6 - - QTabWidget::North + + 6 - - QTabWidget::Rounded + + 6 - - 0 + + 6 - - Qt::ElideNone - - - true - - - false - - - - General - - - + + + + font: 16pt + + + Contact john@dow.org card + + + Qt::AlignCenter + + + + + + + font: italic 8pt; + + + Received 12.07.2007 at 17.35 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + Qt::TabFocus + + + QTabWidget::North + + + QTabWidget::Rounded + + 0 - - 6 + + Qt::ElideNone - - 0 + + true - - 0 + + false - - 6 - - - - - - 0 - 0 - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Personal information</span></p></body></html> - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">General</span></p></body></html> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Organization</span></p></body></html> - - - Qt::AlignCenter - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - Organization name - - - organizationName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Unit / Department - - - organizationDepartment - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Role / Profession - - - organizationRole - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Job title - - - organizationTitle - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - - QLayout::SetDefaultConstraint - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Middle name - - - middleName - - - - - - - First name - - - firstName - - - - - - - Last name - - - lastName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Nick name - - - nickName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Birthday - - - birthday - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - - .. - - - - 0 - 0 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonIconOnly - - - Qt::NoArrow - - - - - - - Qt::Horizontal - - - - - - - - - - - - Full name - - - fullName - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Contact - - - - 0 - - - 0 - - - 6 - - - 0 - - - 0 - - - - - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Contact</span></p></body></html> - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - + + + General + + + 0 - - true + + 6 - - - - 0 - 0 - 582 - 471 - - - - - - - Qt::Horizontal + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Organization</span></p></body></html> + + + Qt::AlignCenter + + + + + + + QLayout::SetDefaultConstraint + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 200 + 0 + - - - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> - - - Qt::AlignCenter + + + 350 + 16777215 + - - - Qt::AlignHCenter|Qt::AlignTop + + + + 200 + 0 + - - - - - 150 - 0 - - - - - 300 - 16777215 - - - - - - - - Jabber ID - - - jabberID - - - - - - - - 150 - 0 - - - - - 300 - 16777215 - - - - - - - - Web site - - - url - - - - - - - - - Qt::Horizontal + + + 350 + 16777215 + - - - - Qt::Horizontal + + + + Middle name + + + middleName + + + + + + + First name + + + firstName + + + + + + + Last name + + + lastName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Nick name + + + nickName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Birthday + + + birthday - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - + - - - - - - - - Description - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Description</span></p></body></html> + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Organization name + + + organizationName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Unit / Department + + + organizationDepartment + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Role / Profession + + + organizationRole + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Job title + + + organizationTitle + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + Full name + + + fullName + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">General</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Personal information</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + .. + + + + 0 + 0 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonIconOnly + + + Qt::NoArrow + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Contact + + + + 0 - - - - - - QFrame::StyledPanel + + 0 - - Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + 6 - - - - - - - - - - QDialogButtonBox::Close|QDialogButtonBox::Save - - - false - - + + 0 + + + 0 + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Contact</span></p></body></html> + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + true + + + + + 0 + 0 + 575 + 475 + + + + + + + Qt::Horizontal + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Jabber ID + + + jabberID + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Web site + + + url + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Description + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Description</span></p></body></html> + + + + + + + QFrame::StyledPanel + + + Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Save + + + false + + + + @@ -806,7 +854,6 @@ organizationDepartment organizationRole organizationTitle - tabWidget jabberID url description From c067835b006bcbd259978e1662b5228f30afee12 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 1 Nov 2019 18:06:03 +0300 Subject: [PATCH 14/20] button to add addresses phones and emails, yet dummy --- ui/widgets/vcard.cpp | 4 + ui/widgets/vcard.ui | 218 ++++++++++++++++++++++++++----------------- 2 files changed, 137 insertions(+), 85 deletions(-) diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard.cpp index 4584638..b092ef8 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard.cpp @@ -69,6 +69,10 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->description->setReadOnly(true); m_ui->url->setReadOnly(true); m_ui->title->setText(tr("Contact %1 card").arg(jid)); + + m_ui->addAddressButton->hide(); + m_ui->addPhoneButton->hide(); + m_ui->addEmailButton->hide(); } connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard.ui index a582d3c..fbd0b82 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard.ui @@ -9,8 +9,8 @@ 0 0 - 594 - 651 + 526 + 662 @@ -560,22 +560,31 @@ 0 0 - 575 - 475 + 514 + 488 - - - - - Qt::Horizontal + + + + + + 0 + 0 + + + + + + + - - + + - + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> @@ -586,17 +595,34 @@ - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + + + + Qt::Vertical - - Qt::AlignCenter + + + 20 + 40 + - + - + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + Qt::AlignHCenter|Qt::AlignTop @@ -655,39 +681,109 @@ - - - - Qt::Horizontal + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - + Qt::Horizontal - + + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Qt::Horizontal - - + + + + + + <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> + + + Qt::AlignCenter + + + + + + + - Qt::Vertical + Qt::Horizontal - - - 20 - 40 - - - + @@ -702,55 +798,7 @@ - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> - - - Qt::AlignCenter - - - - + Qt::Horizontal From 9d491e9e931333df42e62fd41aac9449f05ba033 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 2 Nov 2019 23:50:25 +0300 Subject: [PATCH 15/20] deprecation fix about qxmpp, beginning of adding email addresses in VCards --- CMakeLists.txt | 1 + core/account.cpp | 62 +++---- core/account.h | 2 + global.cpp | 2 + global.h | 1 + ui/CMakeLists.txt | 12 +- ui/squawk.cpp | 14 +- ui/squawk.h | 4 +- ui/utils/comboboxdelegate.cpp | 64 +++++++ ui/utils/comboboxdelegate.h | 47 +++++ ui/widgets/CMakeLists.txt | 29 +++ ui/widgets/vcard/CMakeLists.txt | 21 +++ ui/widgets/vcard/emailsmodel.cpp | 145 +++++++++++++++ ui/widgets/vcard/emailsmodel.h | 57 ++++++ ui/widgets/{ => vcard}/vcard.cpp | 67 ++++++- ui/widgets/{ => vcard}/vcard.h | 17 +- ui/widgets/{ => vcard}/vcard.ui | 292 +++++++++++++------------------ 17 files changed, 610 insertions(+), 227 deletions(-) create mode 100644 ui/utils/comboboxdelegate.cpp create mode 100644 ui/utils/comboboxdelegate.h create mode 100644 ui/widgets/CMakeLists.txt create mode 100644 ui/widgets/vcard/CMakeLists.txt create mode 100644 ui/widgets/vcard/emailsmodel.cpp create mode 100644 ui/widgets/vcard/emailsmodel.h rename ui/widgets/{ => vcard}/vcard.cpp (82%) rename ui/widgets/{ => vcard}/vcard.h (82%) rename ui/widgets/{ => vcard}/vcard.ui (87%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5010adf..f39d0e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) include(GNUInstallDirs) +include_directories(.) find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5LinguistTools) diff --git a/core/account.cpp b/core/account.cpp index f3e4156..ff8c334 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -36,6 +36,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& am(new QXmppMamManager()), mm(new QXmppMucManager()), bm(new QXmppBookmarkManager()), + rm(client.findExtension()), + vm(client.findExtension()), contacts(), conferences(), maxReconnectTimes(0), @@ -57,12 +59,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(&client, &QXmppClient::messageReceived, this, &Account::onMessageReceived); QObject::connect(&client, &QXmppClient::error, this, &Account::onClientError); - QXmppRosterManager& rm = client.rosterManager(); - - QObject::connect(&rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived); - QObject::connect(&rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded); - QObject::connect(&rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved); - QObject::connect(&rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged); + QObject::connect(rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived); + QObject::connect(rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded); + QObject::connect(rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved); + QObject::connect(rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged); //QObject::connect(&rm, &QXmppRosterManager::presenceChanged, this, &Account::onRosterPresenceChanged); client.addExtension(cm); @@ -82,8 +82,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(bm); QObject::connect(bm, &QXmppBookmarkManager::bookmarksReceived, this, &Account::bookmarksReceived); - QXmppVCardManager& vm = client.vCardManager(); - QObject::connect(&vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); + QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); @@ -240,11 +239,10 @@ QString Core::Account::getServer() const void Core::Account::onRosterReceived() { - client.vCardManager().requestClientVCard(); //TODO need to make sure server actually supports vCards + vm->requestClientVCard(); //TODO need to make sure server actually supports vCards ownVCardRequestInProgress = true; - QXmppRosterManager& rm = client.rosterManager(); - QStringList bj = rm.getRosterBareJids(); + QStringList bj = rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { const QString& jid = bj[i]; addedAccount(jid); @@ -264,8 +262,7 @@ void Core::Account::onRosterItemAdded(const QString& bareJid) addedAccount(bareJid); std::map::const_iterator itr = queuedContacts.find(bareJid); if (itr != queuedContacts.end()) { - QXmppRosterManager& rm = client.rosterManager(); - rm.subscribe(bareJid, itr->second); + rm->subscribe(bareJid, itr->second); queuedContacts.erase(itr); } } @@ -278,8 +275,7 @@ void Core::Account::onRosterItemChanged(const QString& bareJid) return; } Contact* contact = itr->second; - QXmppRosterManager& rm = client.rosterManager(); - QXmppRosterIq::Item re = rm.getRosterEntry(bareJid); + QXmppRosterIq::Item re = rm->getRosterEntry(bareJid); Shared::SubscriptionState state = castSubscriptionState(re.subscriptionType()); @@ -308,9 +304,8 @@ void Core::Account::onRosterItemRemoved(const QString& bareJid) void Core::Account::addedAccount(const QString& jid) { - QXmppRosterManager& rm = client.rosterManager(); std::map::const_iterator itr = contacts.find(jid); - QXmppRosterIq::Item re = rm.getRosterEntry(jid); + QXmppRosterIq::Item re = rm->getRosterEntry(jid); Contact* contact; bool newContact = false; if (itr == contacts.end()) { @@ -410,13 +405,13 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) break; case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any if (avatarType.size() > 0) { - client.vCardManager().requestClientVCard(); + vm->requestClientVCard(); ownVCardRequestInProgress = true; } break; case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load if (avatarHash != p_presence.photoHash()) { - client.vCardManager().requestClientVCard(); + vm->requestClientVCard(); ownVCardRequestInProgress = true; } break; @@ -493,7 +488,7 @@ void Core::Account::onRosterPresenceChanged(const QString& bareJid, const QStrin { //not used for now; qDebug() << "presence changed for " << bareJid << " resource " << resource; - const QXmppPresence& presence = client.rosterManager().getPresence(bareJid, resource); + const QXmppPresence& presence = rm->getPresence(bareJid, resource); } void Core::Account::setLogin(const QString& p_login) @@ -1053,8 +1048,7 @@ void Core::Account::onClientError(QXmppClient::Error err) void Core::Account::subscribeToContact(const QString& jid, const QString& reason) { if (state == Shared::connected) { - QXmppRosterManager& rm = client.rosterManager(); - rm.subscribe(jid, reason); + rm->subscribe(jid, reason); } else { qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping"; } @@ -1063,8 +1057,7 @@ void Core::Account::subscribeToContact(const QString& jid, const QString& reason void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) { if (state == Shared::connected) { - QXmppRosterManager& rm = client.rosterManager(); - rm.unsubscribe(jid, reason); + rm->unsubscribe(jid, reason); } else { qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping"; } @@ -1078,8 +1071,7 @@ void Core::Account::removeContactRequest(const QString& jid) outOfRosterContacts.erase(itr); onRosterItemRemoved(jid); } else { - QXmppRosterManager& rm = client.rosterManager(); - rm.removeItem(jid); + rm->removeItem(jid); } } else { qDebug() << "An attempt to remove contact " << jid << " from account " << name << " but the account is not in the connected state, skipping"; @@ -1095,8 +1087,7 @@ void Core::Account::addContactRequest(const QString& jid, const QString& name, c qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is already queued for adding, skipping"; } else { queuedContacts.insert(std::make_pair(jid, "")); //TODO need to add reason here; - QXmppRosterManager& rm = client.rosterManager(); - rm.addItem(jid, name, groups); + rm->addItem(jid, name, groups); } } else { qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is not in the connected state, skipping"; @@ -1273,8 +1264,7 @@ void Core::Account::addContactToGroupRequest(const QString& jid, const QString& if (itr == contacts.end()) { qDebug() << "An attempt to add non existing contact" << jid << "of account" << name << "to the group" << groupName << ", skipping"; } else { - QXmppRosterManager& rm = client.rosterManager(); - QXmppRosterIq::Item item = rm.getRosterEntry(jid); + QXmppRosterIq::Item item = rm->getRosterEntry(jid); QSet groups = item.groups(); if (groups.find(groupName) == groups.end()) { //TODO need to change it, I guess that sort of code is better in qxmpp lib groups.insert(groupName); @@ -1296,8 +1286,7 @@ void Core::Account::removeContactFromGroupRequest(const QString& jid, const QStr if (itr == contacts.end()) { qDebug() << "An attempt to remove non existing contact" << jid << "of account" << name << "from the group" << groupName << ", skipping"; } else { - QXmppRosterManager& rm = client.rosterManager(); - QXmppRosterIq::Item item = rm.getRosterEntry(jid); + QXmppRosterIq::Item item = rm->getRosterEntry(jid); QSet groups = item.groups(); QSet::const_iterator gItr = groups.find(groupName); if (gItr != groups.end()) { @@ -1320,8 +1309,7 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN if (itr == contacts.end()) { qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping"; } else { - QXmppRosterManager& rm = client.rosterManager(); - rm.renameItem(jid, newName); + rm->renameItem(jid, newName); } } @@ -1519,11 +1507,11 @@ void Core::Account::requestVCard(const QString& jid) if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { if (jid == getLogin() + "@" + getServer()) { if (!ownVCardRequestInProgress) { - client.vCardManager().requestClientVCard(); + vm->requestClientVCard(); ownVCardRequestInProgress = true; } } else { - client.vCardManager().requestVCard(jid); + vm->requestVCard(jid); pendingVCardRequests.insert(jid); } } @@ -1596,6 +1584,6 @@ void Core::Account::uploadVCard(const Shared::VCard& card) } } - client.vCardManager().setClientVCard(iq); + vm->setClientVCard(iq); onOwnVCardReceived(iq); } diff --git a/core/account.h b/core/account.h index 371a561..d6444d3 100644 --- a/core/account.h +++ b/core/account.h @@ -125,6 +125,8 @@ private: QXmppMamManager* am; QXmppMucManager* mm; QXmppBookmarkManager* bm; + QXmppRosterManager* rm; + QXmppVCardManager* vm; std::map contacts; std::map conferences; unsigned int maxReconnectTimes; diff --git a/global.cpp b/global.cpp index 9beb7a1..e3ffb11 100644 --- a/global.cpp +++ b/global.cpp @@ -538,6 +538,8 @@ QDateTime Shared::VCard::getReceivingTime() const return receivingTime; } +const std::dequeShared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"}; + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index b046efa..c33bd38 100644 --- a/global.h +++ b/global.h @@ -223,6 +223,7 @@ class VCard { home, work }; + static const std::deque roleNames; Contact(Role p_role = none, bool p_prefered = false); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 59a0a4f..50d5304 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -10,6 +10,8 @@ set(CMAKE_AUTOUIC ON) find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5DBus CONFIG REQUIRED) +add_subdirectory(widgets) + set(squawkUI_SRC squawk.cpp models/accounts.cpp @@ -22,14 +24,6 @@ set(squawkUI_SRC models/room.cpp models/abstractparticipant.cpp models/participant.cpp - widgets/conversation.cpp - widgets/chat.cpp - widgets/room.cpp - widgets/newcontact.cpp - widgets/accounts.cpp - widgets/account.cpp - widgets/joinconference.cpp - widgets/vcard.cpp utils/messageline.cpp utils//message.cpp utils/resizer.cpp @@ -37,11 +31,13 @@ set(squawkUI_SRC utils/flowlayout.cpp utils/badge.cpp utils/progress.cpp + utils/comboboxdelegate.cpp ) # Tell CMake to create the helloworld executable add_library(squawkUI ${squawkUI_SRC}) # Use the Widgets module from Qt 5. +target_link_libraries(squawkUI squawkWidgets) target_link_libraries(squawkUI Qt5::Widgets) target_link_libraries(squawkUI Qt5::DBus) diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 5ddd525..0c27fc6 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -47,14 +47,14 @@ Squawk::Squawk(QWidget *parent) : } m_ui->comboBox->setCurrentIndex(Shared::offline); - connect(m_ui->actionAccounts, SIGNAL(triggered()), this, SLOT(onAccounts())); - connect(m_ui->actionAddContact, SIGNAL(triggered()), this, SLOT(onNewContact())); - connect(m_ui->actionAddConference, SIGNAL(triggered()), this, SLOT(onNewConference())); - connect(m_ui->comboBox, SIGNAL(activated(int)), this, SLOT(onComboboxActivated(int))); - connect(m_ui->roster, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onRosterItemDoubleClicked(const QModelIndex&))); - connect(m_ui->roster, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onRosterContextMenu(const QPoint&))); + connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts); + connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact); + connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference); + connect(m_ui->comboBox, qOverload(&QComboBox::activated), this, &Squawk::onComboboxActivated); + connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked); + connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); - connect(rosterModel.accountsModel, SIGNAL(sizeChanged(unsigned int)), this, SLOT(onAccountsSizeChanged(unsigned int))); + connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); //m_ui->mainToolBar->addWidget(m_ui->comboBox); setWindowTitle(tr("Contact list")); diff --git a/ui/squawk.h b/ui/squawk.h index bf6582f..819d255 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -33,10 +33,10 @@ #include "widgets/room.h" #include "widgets/newcontact.h" #include "widgets/joinconference.h" -#include "widgets/vcard.h" #include "models/roster.h" +#include "widgets/vcard/vcard.h" -#include "../global.h" +#include "global.h" namespace Ui { class Squawk; diff --git a/ui/utils/comboboxdelegate.cpp b/ui/utils/comboboxdelegate.cpp new file mode 100644 index 0000000..824d1cf --- /dev/null +++ b/ui/utils/comboboxdelegate.cpp @@ -0,0 +1,64 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "comboboxdelegate.h" + +ComboboxDelegate::ComboboxDelegate(QObject *parent): + QStyledItemDelegate(parent), + entries() +{ +} + + +ComboboxDelegate::~ComboboxDelegate() +{ +} + + +QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QComboBox *cb = new QComboBox(parent); + + for (const std::pair pair : entries) { + cb->addItem(pair.second, pair.first); + } + + return cb; +} + + +void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + QComboBox *cb = static_cast(editor); + int currentIndex = index.data(Qt::EditRole).toInt(); + if (currentIndex >= 0) { + cb->setCurrentIndex(currentIndex); + } +} + + +void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + QComboBox *cb = static_cast(editor); + model->setData(index, cb->currentIndex(), Qt::EditRole); +} + +void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) +{ + entries.emplace_back(title, icon); +} diff --git a/ui/utils/comboboxdelegate.h b/ui/utils/comboboxdelegate.h new file mode 100644 index 0000000..1d23a5c --- /dev/null +++ b/ui/utils/comboboxdelegate.h @@ -0,0 +1,47 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef COMBOBOXDELEGATE_H +#define COMBOBOXDELEGATE_H + +#include +#include + +#include + +/** + * @todo write docs + */ +class ComboboxDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + ComboboxDelegate(QObject *parent = nullptr); + ~ComboboxDelegate(); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + + void addEntry(const QString& title, const QIcon& icon = QIcon()); + +private: + std::deque> entries; +}; + +#endif // COMBOBOXDELEGATE_H diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt new file mode 100644 index 0000000..29e2dbc --- /dev/null +++ b/ui/widgets/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0) +project(squawkWidgets) + +# 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(Qt5Widgets CONFIG REQUIRED) + +add_subdirectory(vcard) + +set(squawkWidgets_SRC + conversation.cpp + chat.cpp + room.cpp + newcontact.cpp + accounts.cpp + account.cpp + joinconference.cpp +) + +# Tell CMake to create the helloworld executable +add_library(squawkWidgets ${squawkWidgets_SRC}) + +# Use the Widgets module from Qt 5. +target_link_libraries(squawkWidgets vCardUI) +target_link_libraries(squawkWidgets Qt5::Widgets) diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt new file mode 100644 index 0000000..4af3d68 --- /dev/null +++ b/ui/widgets/vcard/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) +project(vCardUI) + +# 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(Qt5Widgets CONFIG REQUIRED) + +set(vCardUI_SRC + vcard.cpp + emailsmodel.cpp +) + +# Tell CMake to create the helloworld executable +add_library(vCardUI ${vCardUI_SRC}) + +# Use the Widgets module from Qt 5. +target_link_libraries(vCardUI Qt5::Widgets) diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/widgets/vcard/emailsmodel.cpp new file mode 100644 index 0000000..4a5024f --- /dev/null +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -0,0 +1,145 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "emailsmodel.h" + +UI::VCard::EMailsModel::EMailsModel(bool p_edit, QObject* parent): + QAbstractTableModel(parent), + edit(p_edit), + deque() +{ +} + +int UI::VCard::EMailsModel::columnCount(const QModelIndex& parent) const +{ + return 3; +} + +int UI::VCard::EMailsModel::rowCount(const QModelIndex& parent) const +{ + return deque.size(); +} + +QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const +{ + if (index.isValid()) { + int col = index.column(); + switch (col) { + case 0: + switch (role) { + case Qt::DisplayRole: + return deque[index.row()].address; + default: + return QVariant(); + } + break; + case 1: + switch (role) { + case Qt::DisplayRole: + return tr(Shared::VCard::Email::roleNames[deque[index.row()].role].toStdString().c_str()); + case Qt::EditRole: + return deque[index.row()].role; + default: + return QVariant(); + } + break; + case 2: + switch (role) { + case Qt::DisplayRole: + return QVariant(); + case Qt::DecorationRole: + if (deque[index.row()].prefered) { + return Shared::icon("favorite", false); + } + return QVariant(); + default: + return QVariant(); + } + break; + default: + return QVariant(); + } + } + return QVariant(); +} + +Qt::ItemFlags UI::VCard::EMailsModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags f = QAbstractTableModel::flags(index); + if (edit && index.column() != 2) { + f = Qt::ItemIsEditable | f; + } + return f; +} + +bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == Qt::EditRole && checkIndex(index)) { + Shared::VCard::Email& item = deque[index.row()]; + switch (index.column()) { + case 0: + item.address = value.toString(); + return true; + case 1: { + quint8 newRole = value.toUInt(); + if (newRole > Shared::VCard::Email::work) { + return false; + } + item.role = static_cast(newRole); + return true; + } + case 2: { + bool newDef = value.toBool(); + if (newDef != item.prefered) { + if (newDef) { + dropPrefered(); + } + item.prefered = newDef; + return true; + } + } + } + return true; + } + return false; +} + + +bool UI::VCard::EMailsModel::dropPrefered() +{ + bool dropped = false; + int i = 0; + for (Shared::VCard::Email& email : deque) { + if (email.prefered) { + email.prefered = false; + QModelIndex ci = createIndex(i, 2, &email); + emit dataChanged(ci, ci); + dropped = true; + } + ++i; + } + return dropped; +} + +QModelIndex UI::VCard::EMailsModel::addNewEmptyLine() +{ + beginInsertRows(QModelIndex(), deque.size(), deque.size()); + deque.emplace_back(""); + endInsertRows(); + return createIndex(deque.size() - 1, 0, &(deque.back())); +} diff --git a/ui/widgets/vcard/emailsmodel.h b/ui/widgets/vcard/emailsmodel.h new file mode 100644 index 0000000..ddb0a57 --- /dev/null +++ b/ui/widgets/vcard/emailsmodel.h @@ -0,0 +1,57 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef UI_VCARD_EMAILSMODEL_H +#define UI_VCARD_EMAILSMODEL_H + +#include +#include + +#include + +#include "global.h" + +namespace UI { +namespace VCard { + +class EMailsModel : public QAbstractTableModel +{ + Q_OBJECT +public: + EMailsModel(bool edit = false, QObject *parent = nullptr); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + +public slots: + QModelIndex addNewEmptyLine(); + +private: + bool edit; + std::deque deque; + +private: + bool dropPrefered(); +}; + +}} + +#endif // UI_VCARD_EMAILSMODEL_H diff --git a/ui/widgets/vcard.cpp b/ui/widgets/vcard/vcard.cpp similarity index 82% rename from ui/widgets/vcard.cpp rename to ui/widgets/vcard/vcard.cpp index b092ef8..3f7778e 100644 --- a/ui/widgets/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -33,7 +33,10 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): currentAvatarPath(""), progress(new Progress(100)), progressLabel(new QLabel()), - overlay(new QWidget()) + overlay(new QWidget()), + contextMenu(new QMenu()), + emails(edit), + roleDelegate(new ComboboxDelegate()) { m_ui->setupUi(this); m_ui->jabberID->setText(jid); @@ -48,6 +51,18 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): setAvatar->setEnabled(true); clearAvatar->setEnabled(false); + roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[0].toStdString().c_str())); + roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[1].toStdString().c_str())); + roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[2].toStdString().c_str())); + + m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->emailsView->setModel(&emails); + m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); + m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); + m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); + if (edit) { avatarMenu = new QMenu(); m_ui->avatarButton->setMenu(avatarMenu); @@ -69,10 +84,6 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->description->setReadOnly(true); m_ui->url->setReadOnly(true); m_ui->title->setText(tr("Contact %1 card").arg(jid)); - - m_ui->addAddressButton->hide(); - m_ui->addPhoneButton->hide(); - m_ui->addEmailButton->hide(); } connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); @@ -106,6 +117,9 @@ VCard::~VCard() if (editable) { avatarMenu->deleteLater(); } + + roleDelegate->deleteLater(); + contextMenu->deleteLater(); } void VCard::setVCard(const QString& jid, const Shared::VCard& card) @@ -253,3 +267,46 @@ void VCard::hideProgress() overlay->hide(); progress->stop(); } + +void VCard::onContextMenu(const QPoint& point) +{ + contextMenu->clear(); + bool hasMenu = false; + QAbstractItemView* snd = static_cast(sender()); + if (snd == m_ui->emailsView) { + if (editable) { + hasMenu = true; + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); + connect(add, &QAction::triggered, this, &VCard::onAddEmail); + } + } + + if (hasMenu) { + contextMenu->popup(snd->viewport()->mapToGlobal(point)); + } +} + +void VCard::onAddEmail() +{ + QModelIndex index = emails.addNewEmptyLine(); + m_ui->emailsView->setCurrentIndex(index); + m_ui->emailsView->edit(index); +} + +void VCard::onAddAddress() +{ + +} +void VCard::onAddPhone() +{ +} +void VCard::onRemoveAddress() +{ +} +void VCard::onRemoveEmail() +{ +} + +void VCard::onRemovePhone() +{ +} diff --git a/ui/widgets/vcard.h b/ui/widgets/vcard/vcard.h similarity index 82% rename from ui/widgets/vcard.h rename to ui/widgets/vcard/vcard.h index 4831734..e150ae9 100644 --- a/ui/widgets/vcard.h +++ b/ui/widgets/vcard/vcard.h @@ -30,11 +30,14 @@ #include #include #include +#include #include -#include "../../global.h" -#include "../utils/progress.h" +#include "global.h" +#include "emailsmodel.h" +#include "ui/utils/progress.h" +#include "ui/utils/comboboxdelegate.h" namespace Ui { @@ -65,6 +68,13 @@ private slots: void onClearAvatar(); void onSetAvatar(); void onAvatarSelected(); + void onAddAddress(); + void onRemoveAddress(); + void onAddEmail(); + void onRemoveEmail(); + void onAddPhone(); + void onRemovePhone(); + void onContextMenu(const QPoint& point); private: QScopedPointer m_ui; @@ -76,6 +86,9 @@ private: Progress* progress; QLabel* progressLabel; QWidget* overlay; + QMenu* contextMenu; + UI::VCard::EMailsModel emails; + ComboboxDelegate* roleDelegate; static const std::set supportedTypes; diff --git a/ui/widgets/vcard.ui b/ui/widgets/vcard/vcard.ui similarity index 87% rename from ui/widgets/vcard.ui rename to ui/widgets/vcard/vcard.ui index fbd0b82..a4381b3 100644 --- a/ui/widgets/vcard.ui +++ b/ui/widgets/vcard/vcard.ui @@ -9,8 +9,8 @@ 0 0 - 526 - 662 + 578 + 671 @@ -84,7 +84,7 @@ QTabWidget::Rounded - 0 + 1 Qt::ElideNone @@ -560,42 +560,72 @@ 0 0 - 514 - 488 + 566 + 497 - - - - - - 0 - 0 - - - - - - - + + + + + Qt::Horizontal - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - + + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + - + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + Qt::Vertical @@ -608,21 +638,43 @@ - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + - + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Qt::AlignHCenter|Qt::AlignTop @@ -681,124 +733,7 @@ - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - - - - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - - <html><head/><body><p><span style=" font-style:italic;">User has no contact e-mail addresses</span></p></body></html> - - - Qt::AlignCenter - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + Qt::Horizontal @@ -811,6 +746,32 @@ + + + + <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + @@ -905,7 +866,6 @@ jabberID url description - scrollArea From 0b57e6a77fc0f83b11ccde1d3785797406cfb16d Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 3 Nov 2019 21:46:40 +0300 Subject: [PATCH 16/20] Refactoring of signal/slots connection to new qt syntax --- core/conference.cpp | 18 ++++++++--------- core/networkaccess.cpp | 6 +++--- signalcatcher.cpp | 2 +- ui/models/accounts.cpp | 4 ++-- ui/models/contact.cpp | 8 ++++---- ui/models/group.cpp | 4 ++-- ui/models/item.cpp | 28 +++++++++++++------------- ui/models/room.cpp | 1 - ui/squawk.cpp | 40 ++++++++++++++++++------------------- ui/utils/badge.cpp | 2 +- ui/utils/message.cpp | 2 +- ui/utils/messageline.cpp | 2 +- ui/widgets/accounts.cpp | 21 ++++++++++--------- ui/widgets/chat.cpp | 2 +- ui/widgets/conversation.cpp | 22 ++++++++++---------- ui/widgets/room.cpp | 2 +- 16 files changed, 81 insertions(+), 83 deletions(-) diff --git a/core/conference.cpp b/core/conference.cpp index 1305419..56a1e8f 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -30,15 +30,15 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo muc = true; name = p_name; - connect(room, SIGNAL(joined()), this, SLOT(onRoomJoined())); - connect(room, SIGNAL(left()), this, SLOT(onRoomLeft())); - connect(room, SIGNAL(nameChanged(const QString&)), this, SLOT(onRoomNameChanged(const QString&))); - connect(room, SIGNAL(subjectChanged(const QString&)), this, SLOT(onRoomSubjectChanged(const QString&))); - connect(room, SIGNAL(participantAdded(const QString&)), this, SLOT(onRoomParticipantAdded(const QString&))); - connect(room, SIGNAL(participantChanged(const QString&)), this, SLOT(onRoomParticipantChanged(const QString&))); - connect(room, SIGNAL(participantRemoved(const QString&)), this, SLOT(onRoomParticipantRemoved(const QString&))); - connect(room, SIGNAL(nickNameChanged(const QString&)), this, SLOT(onRoomNickNameChanged(const QString&))); - connect(room, SIGNAL(error(const QXmppStanza::Error&)), this, SLOT(onRoomError(const QXmppStanza::Error&))); + connect(room, &QXmppMucRoom::joined, this, &Conference::onRoomJoined); + connect(room, &QXmppMucRoom::left, this, &Conference::onRoomLeft); + connect(room, &QXmppMucRoom::nameChanged, this, &Conference::onRoomNameChanged); + connect(room, &QXmppMucRoom::subjectChanged, this, &Conference::onRoomSubjectChanged); + connect(room, &QXmppMucRoom::participantAdded, this, &Conference::onRoomParticipantAdded); + connect(room, &QXmppMucRoom::participantChanged, this, &Conference::onRoomParticipantChanged); + connect(room, &QXmppMucRoom::participantRemoved, this, &Conference::onRoomParticipantRemoved); + connect(room, &QXmppMucRoom::nickNameChanged, this, &Conference::onRoomNickNameChanged); + connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError); room->setNickName(nick); if (autoJoin) { diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 002f9d7..090f4e7 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -323,9 +323,9 @@ void Core::NetworkAccess::startDownload(const QString& messageId, const QString& Download* dwn = new Download({{messageId}, 0, 0, true}); QNetworkRequest req(url); dwn->reply = manager->get(req); - connect(dwn->reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64))); - connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError))); - connect(dwn->reply, SIGNAL(finished()), SLOT(onRequestFinished())); + connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress); + connect(dwn->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onRequestError); + connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onRequestFinished); downloads.insert(std::make_pair(url, dwn)); emit downloadFileProgress(messageId, 0); } diff --git a/signalcatcher.cpp b/signalcatcher.cpp index 3460d0e..9ac3aae 100644 --- a/signalcatcher.cpp +++ b/signalcatcher.cpp @@ -38,7 +38,7 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): } snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); - connect(snInt, SIGNAL(activated(int)), this, SLOT(handleSigInt())); + connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt); } SignalCatcher::~SignalCatcher() diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index b7f16ef..79ed159 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -89,7 +89,7 @@ void Models::Accounts::addAccount(Account* account) } accs.insert(before, account); - connect(account, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onAccountChanged(Models::Item*, int, int))); + connect(account, &Account::childChanged, this, &Accounts::onAccountChanged); endInsertRows(); emit sizeChanged(accs.size()); @@ -143,7 +143,7 @@ void Models::Accounts::removeAccount(int index) { Account* account = accs[index]; beginRemoveRows(QModelIndex(), index, index); - disconnect(account, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onAccountChanged(Models::Item*, int, int))); + disconnect(account, &Account::childChanged, this, &Accounts::onAccountChanged); accs.erase(accs.begin() + index); endRemoveRows(); diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 9b5b436..eee6b4e 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -229,7 +229,7 @@ void Models::Contact::refresh() void Models::Contact::_removeChild(int index) { Item* child = childItems[index]; - disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + disconnect(child, &Item::childChanged, this, &Contact::refresh); Item::_removeChild(index); refresh(); } @@ -237,7 +237,7 @@ void Models::Contact::_removeChild(int index) void Models::Contact::appendChild(Models::Item* child) { Item::appendChild(child); - connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + connect(child, &Item::childChanged, this, &Contact::refresh); refresh(); } @@ -324,7 +324,7 @@ void Models::Contact::toOfflineState() emit childIsAboutToBeRemoved(this, 0, childItems.size()); for (int i = 0; i < childItems.size(); ++i) { Item* item = childItems[i]; - disconnect(item, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + disconnect(item, &Item::childChanged, this, &Contact::refresh); Item::_removeChild(i); item->deleteLater(); } @@ -363,7 +363,7 @@ Models::Contact::Contact(const Models::Contact& other): Presence* pCopy = new Presence(*pres); presences.insert(pCopy->getName(), pCopy); Item::appendChild(pCopy); - connect(pCopy, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + connect(pCopy, &Item::childChanged, this, &Contact::refresh); } refresh(); diff --git a/ui/models/group.cpp b/ui/models/group.cpp index d857710..5e4411e 100644 --- a/ui/models/group.cpp +++ b/ui/models/group.cpp @@ -32,7 +32,7 @@ Models::Group::~Group() void Models::Group::appendChild(Models::Item* child) { Item::appendChild(child); - connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + connect(child, &Item::childChanged, this, &Group::refresh); changed(1); refresh(); } @@ -59,7 +59,7 @@ QVariant Models::Group::data(int column) const void Models::Group::_removeChild(int index) { Item* child = childItems[index]; - disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + disconnect(child, &Item::childChanged, this, &Group::refresh); Item::_removeChild(index); changed(1); refresh(); diff --git a/ui/models/item.cpp b/ui/models/item.cpp index 7ab877a..f90f5e6 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -88,13 +88,13 @@ void Models::Item::appendChild(Models::Item* child) childItems.insert(before, child); child->parent = this; - QObject::connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SIGNAL(childChanged(Models::Item*, int, int))); - QObject::connect(child, SIGNAL(childIsAboutToBeInserted(Item*, int, int)), this, SIGNAL(childIsAboutToBeInserted(Item*, int, int))); - QObject::connect(child, SIGNAL(childInserted()), this, SIGNAL(childInserted())); - QObject::connect(child, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)), this, SIGNAL(childIsAboutToBeRemoved(Item*, int, int))); - QObject::connect(child, SIGNAL(childRemoved()), this, SIGNAL(childRemoved())); - QObject::connect(child, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int))); - QObject::connect(child, SIGNAL(childMoved()), this, SIGNAL(childMoved())); + QObject::connect(child, &Item::childChanged, this, &Item::childChanged); + QObject::connect(child, &Item::childIsAboutToBeInserted, this, &Item::childIsAboutToBeInserted); + QObject::connect(child, &Item::childInserted, this, &Item::childInserted); + QObject::connect(child, &Item::childIsAboutToBeRemoved, this, &Item::childIsAboutToBeRemoved); + QObject::connect(child, &Item::childRemoved, this, &Item::childRemoved); + QObject::connect(child, &Item::childIsAboutToBeMoved, this, &Item::childIsAboutToBeMoved); + QObject::connect(child, &Item::childMoved, this, &Item::childMoved); if (moving) { emit childMoved(); @@ -168,13 +168,13 @@ void Models::Item::_removeChild(int index) { Item* child = childItems[index]; - QObject::disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onChildChanged(Models::Item*, int, int))); - QObject::disconnect(child, SIGNAL(childIsAboutToBeInserted(Item*, int, int)), this, SIGNAL(childIsAboutToBeInserted(Item*, int, int))); - QObject::disconnect(child, SIGNAL(childInserted()), this, SIGNAL(childInserted())); - QObject::disconnect(child, SIGNAL(childIsAboutToBeRemoved(Item*, int, int)), this, SIGNAL(childIsAboutToBeRemoved(Item*, int, int))); - QObject::disconnect(child, SIGNAL(childRemoved()), this, SIGNAL(childRemoved())); - QObject::disconnect(child, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int))); - QObject::disconnect(child, SIGNAL(childMoved()), this, SIGNAL(childMoved())); + QObject::disconnect(child, &Item::childChanged, this, &Item::childChanged); + QObject::disconnect(child, &Item::childIsAboutToBeInserted, this, &Item::childIsAboutToBeInserted); + QObject::disconnect(child, &Item::childInserted, this, &Item::childInserted); + QObject::disconnect(child, &Item::childIsAboutToBeRemoved, this, &Item::childIsAboutToBeRemoved); + QObject::disconnect(child, &Item::childRemoved, this, &Item::childRemoved); + QObject::disconnect(child, &Item::childIsAboutToBeMoved, this, &Item::childIsAboutToBeMoved); + QObject::disconnect(child, &Item::childMoved, this, &Item::childMoved); childItems.erase(childItems.begin() + index); child->parent = 0; diff --git a/ui/models/room.cpp b/ui/models/room.cpp index 6addead..204b2b5 100644 --- a/ui/models/room.cpp +++ b/ui/models/room.cpp @@ -238,7 +238,6 @@ void Models::Room::toOfflineState() emit childIsAboutToBeRemoved(this, 0, childItems.size()); for (int i = 0; i < childItems.size(); ++i) { Item* item = childItems[i]; - disconnect(item, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); Item::_removeChild(i); item->deleteLater(); } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 0c27fc6..396eeb6 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -69,12 +69,12 @@ void Squawk::onAccounts() if (accounts == 0) { accounts = new Accounts(rosterModel.accountsModel, this); accounts->setAttribute(Qt::WA_DeleteOnClose); - connect(accounts, SIGNAL(destroyed(QObject*)), this, SLOT(onAccountsClosed(QObject*))); - connect(accounts, SIGNAL(newAccount(const QMap&)), this, SIGNAL(newAccountRequest(const QMap&))); - connect(accounts, SIGNAL(changeAccount(const QString&, const QMap&)), this, SIGNAL(modifyAccountRequest(const QString&, const QMap&))); - connect(accounts, SIGNAL(connectAccount(const QString&)), this, SIGNAL(connectAccount(const QString&))); - connect(accounts, SIGNAL(disconnectAccount(const QString&)), this, SIGNAL(disconnectAccount(const QString&))); - connect(accounts, SIGNAL(removeAccount(const QString&)), this, SIGNAL(removeAccountRequest(const QString&))); + connect(accounts, &Accounts::destroyed, this, &Squawk::onAccountsClosed); + connect(accounts, &Accounts::newAccount, this, &Squawk::newAccountRequest); + connect(accounts, &Accounts::changeAccount, this, &Squawk::modifyAccountRequest); + connect(accounts, &Accounts::connectAccount, this, &Squawk::connectAccount); + connect(accounts, &Accounts::disconnectAccount, this, &Squawk::disconnectAccount); + connect(accounts, &Accounts::removeAccount, this, &Squawk::removeAccountRequest); accounts->show(); } else { @@ -99,8 +99,8 @@ void Squawk::onNewContact() { NewContact* nc = new NewContact(rosterModel.accountsModel, this); - connect(nc, SIGNAL(accepted()), this, SLOT(onNewContactAccepted())); - connect(nc, SIGNAL(rejected()), nc, SLOT(deleteLater())); + connect(nc, &NewContact::accepted, this, &Squawk::onNewContactAccepted); + connect(nc, &NewContact::rejected, nc, &NewContact::deleteLater); nc->exec(); } @@ -109,8 +109,8 @@ void Squawk::onNewConference() { JoinConference* jc = new JoinConference(rosterModel.accountsModel, this); - connect(jc, SIGNAL(accepted()), this, SLOT(onJoinConferenceAccepted())); - connect(jc, SIGNAL(rejected()), jc, SLOT(deleteLater())); + connect(jc, &JoinConference::accepted, this, &Squawk::onJoinConferenceAccepted); + connect(jc, &JoinConference::rejected, jc, &JoinConference::deleteLater); jc->exec(); } @@ -299,12 +299,12 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) if (created) { conv->setAttribute(Qt::WA_DeleteOnClose); - connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); - connect(conv, SIGNAL(sendMessage(const Shared::Message&)), this, SLOT(onConversationMessage(const Shared::Message&))); - connect(conv, SIGNAL(requestArchive(const QString&)), this, SLOT(onConversationRequestArchive(const QString&))); - connect(conv, SIGNAL(requestLocalFile(const QString&, const QString&)), this, SLOT(onConversationRequestLocalFile(const QString&, const QString&))); - connect(conv, SIGNAL(downloadFile(const QString&, const QString&)), this, SLOT(onConversationDownloadFile(const QString&, const QString&))); - connect(conv, SIGNAL(shown()), this, SLOT(onConversationShown())); + connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); + connect(conv, &Conversation::sendMessage, this, &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); conversations.insert(std::make_pair(*id, conv)); @@ -517,10 +517,10 @@ void Squawk::removeAccount(const QString& account) Conversations::const_iterator lItr = itr; ++itr; Conversation* conv = lItr->second; - disconnect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); - disconnect(conv, SIGNAL(sendMessage(const Shared::Message&)), this, SLOT(onConversationMessage(const Shared::Message&))); - disconnect(conv, SIGNAL(requestArchive(const QString&)), this, SLOT(onConversationRequestArchive(const QString&))); - disconnect(conv, SIGNAL(shown()), this, SLOT(onConversationShown())); + disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); + disconnect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage); + disconnect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive); + disconnect(conv, &Conversation::shown, this, &Squawk::onConversationShown); conv->close(); conversations.erase(lItr); } else { diff --git a/ui/utils/badge.cpp b/ui/utils/badge.cpp index 94277f2..ef15bd2 100644 --- a/ui/utils/badge.cpp +++ b/ui/utils/badge.cpp @@ -42,7 +42,7 @@ Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWid layout->setContentsMargins(2, 2, 2, 2); - connect(closeButton, SIGNAL(clicked()), this, SIGNAL(close())); + connect(closeButton, &QPushButton::clicked, this, &Badge::close); } Badge::~Badge() diff --git a/ui/utils/message.cpp b/ui/utils/message.cpp index 2498d84..951037a 100644 --- a/ui/utils/message.cpp +++ b/ui/utils/message.cpp @@ -125,7 +125,7 @@ void Message::addDownloadDialog() fileComment->setText(tr("%1 is offering you to download a file").arg(sender->text())); } fileComment->show(); - connect(downloadButton, SIGNAL(clicked()), this, SLOT(onDownload())); + connect(downloadButton, &QPushButton::clicked, this, &Message::onDownload); bodyLayout->insertWidget(2, fileComment); bodyLayout->insertWidget(3, downloadButton); hasDownloadButton = true; diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index 57894e8..06efa85 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -127,7 +127,7 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) if (msg.hasOutOfBandUrl()) {\ emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl()); - connect(message, SIGNAL(downloadFile(const QString&, const QString&)), this, SIGNAL(downloadFile(const QString&, const QString&))); + connect(message, &Message::downloadFile, this, &MessageLine::downloadFile); } return res; diff --git a/ui/widgets/accounts.cpp b/ui/widgets/accounts.cpp index cb526cf..62e9ed3 100644 --- a/ui/widgets/accounts.cpp +++ b/ui/widgets/accounts.cpp @@ -29,14 +29,13 @@ Accounts::Accounts(Models::Accounts* p_model, QWidget *parent) : { m_ui->setupUi(this); - connect(m_ui->addButton, SIGNAL(clicked(bool)), this, SLOT(onAddButton(bool))); - connect(m_ui->editButton, SIGNAL(clicked(bool)), this, SLOT(onEditButton(bool))); - connect(m_ui->connectButton, SIGNAL(clicked(bool)), this, SLOT(onConnectButton(bool))); - connect(m_ui->deleteButton, SIGNAL(clicked(bool)), this, SLOT(onDeleteButton(bool))); + connect(m_ui->addButton, &QPushButton::clicked, this, &Accounts::onAddButton); + connect(m_ui->editButton, &QPushButton::clicked, this, &Accounts::onEditButton); + connect(m_ui->connectButton, &QPushButton::clicked, this, &Accounts::onConnectButton); + connect(m_ui->deleteButton, &QPushButton::clicked, this, &Accounts::onDeleteButton); m_ui->tableView->setModel(model); - connect(m_ui->tableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&))); - connect(p_model, SIGNAL(changed()), this, SLOT(updateConnectButton())); + connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Accounts::onSelectionChanged); + connect(p_model, &Models::Accounts::changed, this, &Accounts::updateConnectButton); } Accounts::~Accounts() = default; @@ -44,8 +43,8 @@ Accounts::~Accounts() = default; void Accounts::onAddButton(bool clicked) { Account* acc = new Account(); - connect(acc, SIGNAL(accepted()), this, SLOT(onAccountAccepted())); - connect(acc, SIGNAL(rejected()), this, SLOT(onAccountRejected())); + connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted); + connect(acc, &Account::rejected, this, &Accounts::onAccountRejected); acc->exec(); } @@ -84,8 +83,8 @@ void Accounts::onEditButton(bool clicked) {"resource", mAcc->getResource()} }); acc->lockId(); - connect(acc, SIGNAL(accepted()), this, SLOT(onAccountAccepted())); - connect(acc, SIGNAL(rejected()), this, SLOT(onAccountRejected())); + connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted); + connect(acc, &Account::rejected, this, &Accounts::onAccountRejected); editing = true; acc->exec(); } diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 5e0b390..c876679 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -26,7 +26,7 @@ Chat::Chat(Models::Contact* p_contact, QWidget* parent): updateState(); setStatus(p_contact->getStatus()); - connect(contact, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onContactChanged(Models::Item*, int, int))); + connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); line->setMyName(p_contact->getAccountName()); } diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index c281258..36e7b6e 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -59,15 +59,15 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co statusIcon = m_ui->statusIcon; statusLabel = m_ui->statusLabel; - connect(&ker, SIGNAL(enterPressed()), this, SLOT(onEnterPressed())); - connect(&res, SIGNAL(resized()), this, SLOT(onScrollResize())); - connect(&vis, SIGNAL(shown()), this, SLOT(onScrollResize())); - connect(&vis, SIGNAL(hidden()), this, SLOT(onScrollResize())); - connect(m_ui->sendButton, SIGNAL(clicked(bool)), this, SLOT(onEnterPressed())); - connect(line, SIGNAL(resize(int)), this, SLOT(onMessagesResize(int))); - connect(line, SIGNAL(downloadFile(const QString&, const QString&)), this, SIGNAL(downloadFile(const QString&, const QString&))); - connect(line, SIGNAL(requestLocalFile(const QString&, const QString&)), this, SIGNAL(requestLocalFile(const QString&, const QString&))); - connect(m_ui->attachButton, SIGNAL(clicked(bool)), this, SLOT(onAttach())); + connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); + connect(&res, &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(line, &MessageLine::resize, this, &Conversation::onMessagesResize); + connect(line, &MessageLine::downloadFile, this, &Conversation::downloadFile); + connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile); + connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); m_ui->messageEditor->installEventFilter(&ker); @@ -76,7 +76,7 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co vs->installEventFilter(&vis); vs->setBackgroundRole(QPalette::Base); vs->setAutoFillBackground(true); - connect(vs, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int))); + connect(vs, &QScrollBar::valueChanged, this, &Conversation::onSliderValueChanged); m_ui->scrollArea->installEventFilter(&res); applyVisualEffects(); @@ -317,7 +317,7 @@ void Conversation::addAttachedFile(const QString& path) Badge* badge = new Badge(path, info.fileName(), QIcon::fromTheme(type.iconName())); - connect(badge, SIGNAL(close()), this, SLOT(onBadgeClose())); + connect(badge, &Badge::close, this, &Conversation::onBadgeClose); filesToAttach.push_back(badge); //TODO neet to check if there are any duplicated ids filesLayout->addWidget(badge); if (filesLayout->count() == 1) { diff --git a/ui/widgets/room.cpp b/ui/widgets/room.cpp index b1f9c9c..98fc97d 100644 --- a/ui/widgets/room.cpp +++ b/ui/widgets/room.cpp @@ -26,7 +26,7 @@ Room::Room(Models::Room* p_room, QWidget* parent): line->setMyName(room->getNick()); setStatus(room->getSubject()); - connect(room, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onRoomChanged(Models::Item*, int, int))); + connect(room, &Models::Room::childChanged, this, &Room::onRoomChanged); } Room::~Room() From 5bbacad84a82f55ccf678ce38573c1bb0104431e Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 4 Nov 2019 18:22:39 +0300 Subject: [PATCH 17/20] VCard: email list now displays and stores on server vcard --- core/account.cpp | 56 ++++++++++++++++++++++++++++++++ global.cpp | 30 +++++++++++++++++ global.h | 6 ++++ ui/utils/comboboxdelegate.cpp | 23 ++++++++++++- ui/utils/comboboxdelegate.h | 10 ++++++ ui/widgets/vcard/emailsmodel.cpp | 55 +++++++++++++++++++++++++++++++ ui/widgets/vcard/emailsmodel.h | 6 ++++ ui/widgets/vcard/vcard.cpp | 46 +++++++++++++++++++++++++- ui/widgets/vcard/vcard.ui | 10 +++--- 9 files changed, 235 insertions(+), 7 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index ff8c334..ed6da44 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -1361,6 +1361,23 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) vCard.setOrgUnit(org.unit()); vCard.setOrgTitle(org.title()); + QList emails = card.emails(); + std::deque& myEmails = vCard.getEmails(); + for (const QXmppVCardEmail& em : emails) { + Shared::VCard::Email mEm(em.address()); + QXmppVCardEmail::Type et = em.type(); + if (et & QXmppVCardEmail::Preferred) { + mEm.prefered = true; + } + if (et & QXmppVCardEmail::Home && !(et & QXmppVCardEmail::Work)) { + mEm.role = Shared::VCard::Email::home; + } else if (!(et & QXmppVCardEmail::Home) && et & QXmppVCardEmail::Work) { + mEm.role = Shared::VCard::Email::work; + } + + myEmails.emplace_back(mEm); + } + if (item->hasAvatar()) { if (!item->isAvatarAutoGenerated()) { vCard.setAvatarType(Shared::Avatar::valid); @@ -1483,6 +1500,24 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) vCard.setAvatarType(Shared::Avatar::empty); } + + QList emails = card.emails(); + std::deque& myEmails = vCard.getEmails(); + for (const QXmppVCardEmail& em : emails) { + Shared::VCard::Email mEm(em.address()); + QXmppVCardEmail::Type et = em.type(); + if (et & QXmppVCardEmail::Preferred) { + mEm.prefered = true; + } + if (et & QXmppVCardEmail::Home && !(et & QXmppVCardEmail::Work)) { + mEm.role = Shared::VCard::Email::home; + } else if (!(et & QXmppVCardEmail::Home) && et & QXmppVCardEmail::Work) { + mEm.role = Shared::VCard::Email::work; + } + + myEmails.emplace_back(mEm); + } + emit receivedVCard(getLogin() + "@" + getServer(), vCard); } @@ -1535,6 +1570,27 @@ void Core::Account::uploadVCard(const Shared::VCard& card) org.setTitle(card.getOrgTitle()); iq.setOrganization(org); + const std::deque& myEmails = card.getEmails(); + QList emails; + for (const Shared::VCard::Email& mEm : myEmails) { + QXmppVCardEmail em; + QXmppVCardEmail::Type t = QXmppVCardEmail::Internet; + if (mEm.prefered) { + t = t | QXmppVCardEmail::Preferred; + } + if (mEm.role == Shared::VCard::Email::home) { + t = t | QXmppVCardEmail::Home; + } else if (mEm.role == Shared::VCard::Email::work) { + t = t | QXmppVCardEmail::Work; + } + em.setType(t); + em.setAddress(mEm.address); + + emails.push_back(em); + } + + iq.setEmails(emails); + bool avatarChanged = false; if (card.getAvatarType() == Shared::Avatar::empty) { if (avatarType.size() > 0) { diff --git a/global.cpp b/global.cpp index e3ffb11..e223a45 100644 --- a/global.cpp +++ b/global.cpp @@ -538,6 +538,36 @@ QDateTime Shared::VCard::getReceivingTime() const return receivingTime; } +std::deque & Shared::VCard::getEmails() +{ + return emails; +} + +std::deque & Shared::VCard::getAddresses() +{ + return addresses; +} + +std::deque & Shared::VCard::getPhones() +{ + return phones; +} + +const std::deque & Shared::VCard::getEmails() const +{ + return emails; +} + +const std::deque & Shared::VCard::getAddresses() const +{ + return addresses; +} + +const std::deque & Shared::VCard::getPhones() const +{ + return phones; +} + const std::dequeShared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"}; QIcon Shared::availabilityIcon(Shared::Availability av, bool big) diff --git a/global.h b/global.h index c33bd38..34d892c 100644 --- a/global.h +++ b/global.h @@ -302,6 +302,12 @@ public: QString getOrgTitle() const; void setOrgTitle(const QString& title); QDateTime getReceivingTime() const; + std::deque& getEmails(); + const std::deque& getEmails() const; + std::deque& getPhones(); + const std::deque& getPhones() const; + std::deque
& getAddresses(); + const std::deque
& getAddresses() const; private: QString fullName; diff --git a/ui/utils/comboboxdelegate.cpp b/ui/utils/comboboxdelegate.cpp index 824d1cf..7153405 100644 --- a/ui/utils/comboboxdelegate.cpp +++ b/ui/utils/comboboxdelegate.cpp @@ -15,18 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "QTimer" #include "comboboxdelegate.h" ComboboxDelegate::ComboboxDelegate(QObject *parent): QStyledItemDelegate(parent), - entries() + entries(), + ff(new FocusFilter()) { } ComboboxDelegate::~ComboboxDelegate() { + delete ff; } @@ -48,6 +51,7 @@ void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) int currentIndex = index.data(Qt::EditRole).toInt(); if (currentIndex >= 0) { cb->setCurrentIndex(currentIndex); + cb->installEventFilter(ff); } } @@ -62,3 +66,20 @@ void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) { entries.emplace_back(title, icon); } + +bool ComboboxDelegate::FocusFilter::eventFilter(QObject* src, QEvent* evt) +{ + if (evt->type() == QEvent::FocusIn) { + QComboBox* cb = static_cast(src); + cb->removeEventFilter(this); + QTimer* timer = new QTimer; //TODO that is ridiculous! I refuse to believe there is no better way than that one! + QObject::connect(timer, &QTimer::timeout, [timer, cb]() { + cb->showPopup(); + timer->deleteLater(); + }); + + timer->setSingleShot(true); + timer->start(100); + } + return QObject::eventFilter(src, evt); +} diff --git a/ui/utils/comboboxdelegate.h b/ui/utils/comboboxdelegate.h index 1d23a5c..a5d79e4 100644 --- a/ui/utils/comboboxdelegate.h +++ b/ui/utils/comboboxdelegate.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -30,6 +31,12 @@ class ComboboxDelegate : public QStyledItemDelegate { Q_OBJECT + + class FocusFilter : public QObject { + public: + bool eventFilter(QObject *src, QEvent *evt) override; + }; + public: ComboboxDelegate(QObject *parent = nullptr); ~ComboboxDelegate(); @@ -42,6 +49,9 @@ public: private: std::deque> entries; + FocusFilter* ff; }; + + #endif // COMBOBOXDELEGATE_H diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/widgets/vcard/emailsmodel.cpp index 4a5024f..2babaad 100644 --- a/ui/widgets/vcard/emailsmodel.cpp +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -43,6 +43,7 @@ QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const case 0: switch (role) { case Qt::DisplayRole: + case Qt::EditRole: return deque[index.row()].address; default: return QVariant(); @@ -143,3 +144,57 @@ QModelIndex UI::VCard::EMailsModel::addNewEmptyLine() endInsertRows(); return createIndex(deque.size() - 1, 0, &(deque.back())); } + +bool UI::VCard::EMailsModel::isPreferred(int row) const +{ + if (row < deque.size()) { + return deque[row].prefered; + } else { + return false; + } +} + +void UI::VCard::EMailsModel::removeLines(int index, int count) +{ + if (index < deque.size()) { + int maxCount = deque.size() - index; + if (count > maxCount) { + count = maxCount; + } + + if (count > 0) { + beginRemoveRows(QModelIndex(), index, index + count - 1); + std::deque::const_iterator itr = deque.begin() + index; + std::deque::const_iterator end = itr + count; + deque.erase(itr, end); + endRemoveRows(); + } + } +} + +void UI::VCard::EMailsModel::getEmails(std::deque& emails) const +{ + for (const Shared::VCard::Email& my : deque) { + emails.emplace_back(my); + } +} + +void UI::VCard::EMailsModel::setEmails(const std::deque& emails) +{ + if (deque.size() > 0) { + removeLines(0, deque.size()); + } + + if (emails.size() > 0) { + beginInsertRows(QModelIndex(), 0, emails.size() - 1); + for (const Shared::VCard::Email& comming : emails) { + deque.emplace_back(comming); + } + endInsertRows(); + } +} + +void UI::VCard::EMailsModel::revertPreferred(int row) +{ + setData(createIndex(row, 2), !isPreferred(row)); +} diff --git a/ui/widgets/vcard/emailsmodel.h b/ui/widgets/vcard/emailsmodel.h index ddb0a57..10a610f 100644 --- a/ui/widgets/vcard/emailsmodel.h +++ b/ui/widgets/vcard/emailsmodel.h @@ -40,9 +40,15 @@ public: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; + bool isPreferred(int row) const; + + void removeLines(int index, int count); + void setEmails(const std::deque& emails); + void getEmails(std::deque& emails) const; public slots: QModelIndex addNewEmptyLine(); + void revertPreferred(int row); private: bool edit; diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp index 3f7778e..850830d 100644 --- a/ui/widgets/vcard/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -21,6 +21,8 @@ #include +#include + const std::set VCard::supportedTypes = {"image/jpeg", "image/png"}; VCard::VCard(const QString& jid, bool edit, QWidget* parent): @@ -58,6 +60,7 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); m_ui->emailsView->setModel(&emails); m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); + m_ui->emailsView->setColumnWidth(2, 30); m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); @@ -68,7 +71,7 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): m_ui->avatarButton->setMenu(avatarMenu); avatarMenu->addAction(setAvatar); avatarMenu->addAction(clearAvatar); - m_ui->title->setText(tr("Your card")); + m_ui->title->setText(tr("Account %1 card").arg(jid)); } else { m_ui->buttonBox->hide(); m_ui->fullName->setReadOnly(true); @@ -149,6 +152,9 @@ void VCard::setVCard(const Shared::VCard& card) currentAvatarPath = card.getAvatarPath(); updateAvatar(); + + const std::deque& ems = card.getEmails(); + emails.setEmails(ems); } QString VCard::getJid() const @@ -174,6 +180,8 @@ void VCard::onButtonBoxAccepted() card.setAvatarPath(currentAvatarPath); card.setAvatarType(currentAvatarType); + emails.getEmails(card.getEmails()); + emit saveVCard(card); } @@ -278,6 +286,25 @@ void VCard::onContextMenu(const QPoint& point) hasMenu = true; QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); connect(add, &QAction::triggered, this, &VCard::onAddEmail); + + QItemSelectionModel* sm = m_ui->emailsView->selectionModel(); + int selectionSize = sm->selectedRows().size(); + + if (selectionSize > 0) { + if (selectionSize == 1) { + int row = sm->selectedRows().at(0).row(); + if (emails.isPreferred(row)) { + QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this email as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); + } else { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); + } + } + + QAction* del = contextMenu->addAction(Shared::icon("remove"), tr("Remove selected email addresses")); + connect(del, &QAction::triggered, this, &VCard::onRemoveEmail); + } } } @@ -305,6 +332,23 @@ void VCard::onRemoveAddress() } void VCard::onRemoveEmail() { + QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); + + QList rows; + for (const QModelIndex& index : selection.indexes()) { + rows.append(index.row()); + } + + std::sort(rows.begin(), rows.end()); + + int prev = -1; + for (int i = rows.count() - 1; i >= 0; i -= 1) { + int current = rows[i]; + if (current != prev) { + emails.removeLines(current, 1); + prev = current; + } + } } void VCard::onRemovePhone() diff --git a/ui/widgets/vcard/vcard.ui b/ui/widgets/vcard/vcard.ui index a4381b3..e08bf49 100644 --- a/ui/widgets/vcard/vcard.ui +++ b/ui/widgets/vcard/vcard.ui @@ -84,7 +84,7 @@ QTabWidget::Rounded - 1 + 0 Qt::ElideNone @@ -564,7 +564,7 @@ 497 - + @@ -644,7 +644,7 @@ <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignCenter @@ -670,7 +670,7 @@ <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignCenter @@ -752,7 +752,7 @@ <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignCenter From c1c1de1b7bacc9ee9661021fef306fcd027e51c6 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 5 Nov 2019 21:55:21 +0300 Subject: [PATCH 18/20] backend adapter for vCard phones list --- core/CMakeLists.txt | 1 + core/account.cpp | 100 +----------- core/account.h | 2 + core/adapterFuctions.cpp | 269 +++++++++++++++++++++++++++++++ global.h | 5 +- ui/widgets/vcard/emailsmodel.cpp | 2 +- 6 files changed, 281 insertions(+), 98 deletions(-) create mode 100644 core/adapterFuctions.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 2c0374d..fab36f2 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -17,6 +17,7 @@ set(squawkCORE_SRC conference.cpp storage.cpp networkaccess.cpp + adapterFuctions.cpp ) # Tell CMake to create the helloworld executable diff --git a/core/account.cpp b/core/account.cpp index ed6da44..e3dd29f 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -1347,36 +1347,7 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card) } Shared::VCard vCard; - vCard.setFullName(card.fullName()); - vCard.setFirstName(card.firstName()); - vCard.setMiddleName(card.middleName()); - vCard.setLastName(card.lastName()); - vCard.setBirthday(card.birthday()); - vCard.setNickName(card.nickName()); - vCard.setDescription(card.description()); - vCard.setUrl(card.url()); - QXmppVCardOrganization org = card.organization(); - vCard.setOrgName(org.organization()); - vCard.setOrgRole(org.role()); - vCard.setOrgUnit(org.unit()); - vCard.setOrgTitle(org.title()); - - QList emails = card.emails(); - std::deque& myEmails = vCard.getEmails(); - for (const QXmppVCardEmail& em : emails) { - Shared::VCard::Email mEm(em.address()); - QXmppVCardEmail::Type et = em.type(); - if (et & QXmppVCardEmail::Preferred) { - mEm.prefered = true; - } - if (et & QXmppVCardEmail::Home && !(et & QXmppVCardEmail::Work)) { - mEm.role = Shared::VCard::Email::home; - } else if (!(et & QXmppVCardEmail::Home) && et & QXmppVCardEmail::Work) { - mEm.role = Shared::VCard::Email::work; - } - - myEmails.emplace_back(mEm); - } + initializeVCard(vCard, card); if (item->hasAvatar()) { if (!item->isAvatarAutoGenerated()) { @@ -1480,19 +1451,8 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) ownVCardRequestInProgress = false; Shared::VCard vCard; - vCard.setFullName(card.fullName()); - vCard.setFirstName(card.firstName()); - vCard.setMiddleName(card.middleName()); - vCard.setLastName(card.lastName()); - vCard.setBirthday(card.birthday()); - vCard.setNickName(card.nickName()); - vCard.setDescription(card.description()); - vCard.setUrl(card.url()); - QXmppVCardOrganization org = card.organization(); - vCard.setOrgName(org.organization()); - vCard.setOrgRole(org.role()); - vCard.setOrgUnit(org.unit()); - vCard.setOrgTitle(org.title()); + initializeVCard(vCard, card); + if (avatarType.size() > 0) { vCard.setAvatarType(Shared::Avatar::valid); vCard.setAvatarPath(path + "avatar." + avatarType); @@ -1500,24 +1460,6 @@ void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) vCard.setAvatarType(Shared::Avatar::empty); } - - QList emails = card.emails(); - std::deque& myEmails = vCard.getEmails(); - for (const QXmppVCardEmail& em : emails) { - Shared::VCard::Email mEm(em.address()); - QXmppVCardEmail::Type et = em.type(); - if (et & QXmppVCardEmail::Preferred) { - mEm.prefered = true; - } - if (et & QXmppVCardEmail::Home && !(et & QXmppVCardEmail::Work)) { - mEm.role = Shared::VCard::Email::home; - } else if (!(et & QXmppVCardEmail::Home) && et & QXmppVCardEmail::Work) { - mEm.role = Shared::VCard::Email::work; - } - - myEmails.emplace_back(mEm); - } - emit receivedVCard(getLogin() + "@" + getServer(), vCard); } @@ -1555,41 +1497,7 @@ void Core::Account::requestVCard(const QString& jid) void Core::Account::uploadVCard(const Shared::VCard& card) { QXmppVCardIq iq; - iq.setFullName(card.getFullName()); - iq.setFirstName(card.getFirstName()); - iq.setMiddleName(card.getMiddleName()); - iq.setLastName(card.getLastName()); - iq.setNickName(card.getNickName()); - iq.setBirthday(card.getBirthday()); - iq.setDescription(card.getDescription()); - iq.setUrl(card.getUrl()); - QXmppVCardOrganization org; - org.setOrganization(card.getOrgName()); - org.setUnit(card.getOrgUnit()); - org.setRole(card.getOrgRole()); - org.setTitle(card.getOrgTitle()); - iq.setOrganization(org); - - const std::deque& myEmails = card.getEmails(); - QList emails; - for (const Shared::VCard::Email& mEm : myEmails) { - QXmppVCardEmail em; - QXmppVCardEmail::Type t = QXmppVCardEmail::Internet; - if (mEm.prefered) { - t = t | QXmppVCardEmail::Preferred; - } - if (mEm.role == Shared::VCard::Email::home) { - t = t | QXmppVCardEmail::Home; - } else if (mEm.role == Shared::VCard::Email::work) { - t = t | QXmppVCardEmail::Work; - } - em.setType(t); - em.setAddress(mEm.address); - - emails.push_back(em); - } - - iq.setEmails(emails); + initializeQXmppVCard(iq, card); bool avatarChanged = false; if (card.getAvatarType() == Shared::Avatar::empty) { diff --git a/core/account.h b/core/account.h index d6444d3..a5f07bd 100644 --- a/core/account.h +++ b/core/account.h @@ -202,6 +202,8 @@ private: }; +void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); +void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); } diff --git a/core/adapterFuctions.cpp b/core/adapterFuctions.cpp new file mode 100644 index 0000000..0693c34 --- /dev/null +++ b/core/adapterFuctions.cpp @@ -0,0 +1,269 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ +#ifndef CORE_ADAPTER_FUNCTIONS_H +#define CORE_ADAPTER_FUNCTIONS_H + +#include "account.h" + +void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card) +{ + vCard.setFullName(card.fullName()); + vCard.setFirstName(card.firstName()); + vCard.setMiddleName(card.middleName()); + vCard.setLastName(card.lastName()); + vCard.setBirthday(card.birthday()); + vCard.setNickName(card.nickName()); + vCard.setDescription(card.description()); + vCard.setUrl(card.url()); + QXmppVCardOrganization org = card.organization(); + vCard.setOrgName(org.organization()); + vCard.setOrgRole(org.role()); + vCard.setOrgUnit(org.unit()); + vCard.setOrgTitle(org.title()); + + QList emails = card.emails(); + std::deque& myEmails = vCard.getEmails(); + for (const QXmppVCardEmail& em : emails) { + QXmppVCardEmail::Type et = em.type(); + bool prefered = false; + bool accounted = false; + if (et & QXmppVCardEmail::Preferred) { + prefered = true; + } + if (et & QXmppVCardEmail::Home) { + myEmails.emplace_back(em.address(), Shared::VCard::Email::home, prefered); + accounted = true; + } + if (et & QXmppVCardEmail::Work) { + myEmails.emplace_back(em.address(), Shared::VCard::Email::work, prefered); + accounted = true; + } + if (!accounted) { + myEmails.emplace_back(em.address(), Shared::VCard::Email::none, prefered); + } + + } + + QList phones = card.phones(); + std::deque& myPhones = vCard.getPhones(); + for (const QXmppVCardPhone& ph : phones) { + Shared::VCard::Phone mPh(ph.number()); + QXmppVCardPhone::Type pt = ph.type(); + bool prefered = false; + bool accounted = false; + if (pt & QXmppVCardPhone::Preferred) { + prefered = true; + } + + bool home = false; + bool work = false; + + if (pt & QXmppVCardPhone::Home) { + home = true; + } + if (pt & QXmppVCardPhone::Work) { + work = true; + } + + + if (pt & QXmppVCardPhone::Fax) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::fax, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::fax, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::fax, Shared::VCard::Phone::none, prefered); + } + accounted = true; + } + if (pt & QXmppVCardPhone::Voice) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::voice, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::voice, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::voice, Shared::VCard::Phone::none, prefered); + } + accounted = true; + } + if (pt & QXmppVCardPhone::Pager) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::pager, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::pager, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::pager, Shared::VCard::Phone::none, prefered); + } + accounted = true; + } + if (pt & QXmppVCardPhone::Cell) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::cell, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::cell, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::cell, Shared::VCard::Phone::none, prefered); + } + accounted = true; + } + if (pt & QXmppVCardPhone::Video) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::video, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::video, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::video, Shared::VCard::Phone::none, prefered); + } + accounted = true; + } + if (pt & QXmppVCardPhone::Modem) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::modem, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::modem, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::modem, Shared::VCard::Phone::none, prefered); + } + accounted = true; + } + if (!accounted) { + if (home || work) { + if (home) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::other, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::other, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(ph.number(), Shared::VCard::Phone::other, Shared::VCard::Phone::none, prefered); + } + } + } +} + +void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) { + iq.setFullName(card.getFullName()); + iq.setFirstName(card.getFirstName()); + iq.setMiddleName(card.getMiddleName()); + iq.setLastName(card.getLastName()); + iq.setNickName(card.getNickName()); + iq.setBirthday(card.getBirthday()); + iq.setDescription(card.getDescription()); + iq.setUrl(card.getUrl()); + QXmppVCardOrganization org; + org.setOrganization(card.getOrgName()); + org.setUnit(card.getOrgUnit()); + org.setRole(card.getOrgRole()); + org.setTitle(card.getOrgTitle()); + iq.setOrganization(org); + + const std::deque& myEmails = card.getEmails(); + QList emails; + for (const Shared::VCard::Email& mEm : myEmails) { + QXmppVCardEmail em; + QXmppVCardEmail::Type t = QXmppVCardEmail::Internet; + if (mEm.prefered) { + t = t | QXmppVCardEmail::Preferred; + } + if (mEm.role == Shared::VCard::Email::home) { + t = t | QXmppVCardEmail::Home; + } else if (mEm.role == Shared::VCard::Email::work) { + t = t | QXmppVCardEmail::Work; + } + em.setType(t); + em.setAddress(mEm.address); + + emails.push_back(em); + } + + std::map phones; + QList phs; + const std::deque& myPhones = card.getPhones(); + for (const Shared::VCard::Phone& mPh : myPhones) { + std::map::iterator itr = phones.find(mPh.number); + if (itr == phones.end()) { + itr = phones.emplace(mPh.number, QXmppVCardPhone()).first; + } + QXmppVCardPhone& phone = itr->second; + + switch (mPh.type) { + case Shared::VCard::Phone::fax: + phone.setType(phone.type() | QXmppVCardPhone::Fax); + break; + case Shared::VCard::Phone::pager: + phone.setType(phone.type() | QXmppVCardPhone::Pager); + break; + case Shared::VCard::Phone::voice: + phone.setType(phone.type() | QXmppVCardPhone::Voice); + break; + case Shared::VCard::Phone::cell: + phone.setType(phone.type() | QXmppVCardPhone::Cell); + break; + case Shared::VCard::Phone::video: + phone.setType(phone.type() | QXmppVCardPhone::Video); + break; + case Shared::VCard::Phone::modem: + phone.setType(phone.type() | QXmppVCardPhone::Modem); + break; + case Shared::VCard::Phone::other: + phone.setType(phone.type() | QXmppVCardPhone::PCS); //loss of information, but I don't even know what the heck is this type of phone! + break; + } + + switch (mPh.role) { + case Shared::VCard::Phone::home: + phone.setType(phone.type() | QXmppVCardPhone::Home); + break; + case Shared::VCard::Phone::work: + phone.setType(phone.type() | QXmppVCardPhone::Work); + break; + default: + break; + } + + if (mPh.prefered) { + phone.setType(phone.type() | QXmppVCardPhone::Preferred); + } + } + for (const std::pair& phone : phones) { + phs.push_back(phone.second); + } + + iq.setEmails(emails); + iq.setPhones(phs); +} + +#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/global.h b/global.h index 34d892c..72c8406 100644 --- a/global.h +++ b/global.h @@ -238,13 +238,15 @@ public: QString address; }; class Phone : public Contact { + public: enum Type { fax, pager, voice, cell, video, - modem + modem, + other }; Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false); @@ -252,6 +254,7 @@ public: Type type; }; class Address : public Contact { + public: Address( const QString& zCode = "", const QString& cntry = "", diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/widgets/vcard/emailsmodel.cpp index 2babaad..7e3a646 100644 --- a/ui/widgets/vcard/emailsmodel.cpp +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -108,7 +108,7 @@ bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& v bool newDef = value.toBool(); if (newDef != item.prefered) { if (newDef) { - dropPrefered(); + //dropPrefered(); } item.prefered = newDef; return true; From 2c13f0d77c47aa871676fa35e50ea54e6a8859e9 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 6 Nov 2019 18:14:49 +0300 Subject: [PATCH 19/20] phones now also saveble --- core/adapterFuctions.cpp | 1 + global.cpp | 1 + global.h | 1 + ui/widgets/vcard/CMakeLists.txt | 1 + ui/widgets/vcard/emailsmodel.cpp | 5 + ui/widgets/vcard/emailsmodel.h | 1 + ui/widgets/vcard/phonesmodel.cpp | 222 +++++++++++++++++++++++++++++++ ui/widgets/vcard/phonesmodel.h | 65 +++++++++ ui/widgets/vcard/vcard.cpp | 112 +++++++++++++++- ui/widgets/vcard/vcard.h | 7 + 10 files changed, 413 insertions(+), 3 deletions(-) create mode 100644 ui/widgets/vcard/phonesmodel.cpp create mode 100644 ui/widgets/vcard/phonesmodel.h diff --git a/core/adapterFuctions.cpp b/core/adapterFuctions.cpp index 0693c34..0279533 100644 --- a/core/adapterFuctions.cpp +++ b/core/adapterFuctions.cpp @@ -218,6 +218,7 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) { itr = phones.emplace(mPh.number, QXmppVCardPhone()).first; } QXmppVCardPhone& phone = itr->second; + phone.setNumber(mPh.number); switch (mPh.type) { case Shared::VCard::Phone::fax: diff --git a/global.cpp b/global.cpp index e223a45..d054150 100644 --- a/global.cpp +++ b/global.cpp @@ -569,6 +569,7 @@ const std::deque & Shared::VCard::getPhones() const } const std::dequeShared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"}; +const std::dequeShared::VCard::Phone::typeNames = {"Fax", "Pager", "Voice", "Cell", "Video", "Modem", "Other"}; QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { diff --git a/global.h b/global.h index 72c8406..ccb7317 100644 --- a/global.h +++ b/global.h @@ -248,6 +248,7 @@ public: modem, other }; + static const std::deque typeNames; Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false); QString number; diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt index 4af3d68..4d2ee15 100644 --- a/ui/widgets/vcard/CMakeLists.txt +++ b/ui/widgets/vcard/CMakeLists.txt @@ -12,6 +12,7 @@ find_package(Qt5Widgets CONFIG REQUIRED) set(vCardUI_SRC vcard.cpp emailsmodel.cpp + phonesmodel.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/widgets/vcard/emailsmodel.cpp index 7e3a646..18838ee 100644 --- a/ui/widgets/vcard/emailsmodel.cpp +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -198,3 +198,8 @@ void UI::VCard::EMailsModel::revertPreferred(int row) { setData(createIndex(row, 2), !isPreferred(row)); } + +QString UI::VCard::EMailsModel::getEmail(int row) const +{ + return deque[row].address; +} diff --git a/ui/widgets/vcard/emailsmodel.h b/ui/widgets/vcard/emailsmodel.h index 10a610f..358536f 100644 --- a/ui/widgets/vcard/emailsmodel.h +++ b/ui/widgets/vcard/emailsmodel.h @@ -45,6 +45,7 @@ public: void removeLines(int index, int count); void setEmails(const std::deque& emails); void getEmails(std::deque& emails) const; + QString getEmail(int row) const; public slots: QModelIndex addNewEmptyLine(); diff --git a/ui/widgets/vcard/phonesmodel.cpp b/ui/widgets/vcard/phonesmodel.cpp new file mode 100644 index 0000000..4371dff --- /dev/null +++ b/ui/widgets/vcard/phonesmodel.cpp @@ -0,0 +1,222 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "phonesmodel.h" + +UI::VCard::PhonesModel::PhonesModel(bool p_edit, QObject* parent): + QAbstractTableModel(parent), + edit(p_edit), + deque() +{ +} + +int UI::VCard::PhonesModel::columnCount(const QModelIndex& parent) const +{ + return 4; +} + +int UI::VCard::PhonesModel::rowCount(const QModelIndex& parent) const +{ + return deque.size(); +} + +QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const +{ + if (index.isValid()) { + int col = index.column(); + switch (col) { + case 0: + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: + return deque[index.row()].number; + default: + return QVariant(); + } + break; + case 1: + switch (role) { + case Qt::DisplayRole: + return tr(Shared::VCard::Phone::roleNames[deque[index.row()].role].toStdString().c_str()); + case Qt::EditRole: + return deque[index.row()].role; + default: + return QVariant(); + } + break; + case 2: + switch (role) { + case Qt::DisplayRole: + return tr(Shared::VCard::Phone::typeNames[deque[index.row()].type].toStdString().c_str()); + case Qt::EditRole: + return deque[index.row()].type; + default: + return QVariant(); + } + break; + case 3: + switch (role) { + case Qt::DisplayRole: + return QVariant(); + case Qt::DecorationRole: + if (deque[index.row()].prefered) { + return Shared::icon("favorite", false); + } + return QVariant(); + default: + return QVariant(); + } + break; + default: + return QVariant(); + } + } + return QVariant(); +} + +QModelIndex UI::VCard::PhonesModel::addNewEmptyLine() +{ + beginInsertRows(QModelIndex(), deque.size(), deque.size()); + deque.emplace_back("", Shared::VCard::Phone::other); + endInsertRows(); + return createIndex(deque.size() - 1, 0, &(deque.back())); +} + +Qt::ItemFlags UI::VCard::PhonesModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags f = QAbstractTableModel::flags(index); + if (edit && index.column() != 3) { + f = Qt::ItemIsEditable | f; + } + return f; +} + +bool UI::VCard::PhonesModel::dropPrefered() +{ + bool dropped = false; + int i = 0; + for (Shared::VCard::Phone& phone : deque) { + if (phone.prefered) { + phone.prefered = false; + QModelIndex ci = createIndex(i, 2, &phone); + emit dataChanged(ci, ci); + dropped = true; + } + ++i; + } + return dropped; +} + +void UI::VCard::PhonesModel::getPhones(std::deque& phones) const +{ + for (const Shared::VCard::Phone& my : deque) { + phones.emplace_back(my); + } +} + +bool UI::VCard::PhonesModel::isPreferred(int row) const +{ + if (row < deque.size()) { + return deque[row].prefered; + } else { + return false; + } +} + +void UI::VCard::PhonesModel::removeLines(int index, int count) +{ + if (index < deque.size()) { + int maxCount = deque.size() - index; + if (count > maxCount) { + count = maxCount; + } + + if (count > 0) { + beginRemoveRows(QModelIndex(), index, index + count - 1); + std::deque::const_iterator itr = deque.begin() + index; + std::deque::const_iterator end = itr + count; + deque.erase(itr, end); + endRemoveRows(); + } + } +} + +void UI::VCard::PhonesModel::revertPreferred(int row) +{ + setData(createIndex(row, 3), !isPreferred(row)); +} + +bool UI::VCard::PhonesModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == Qt::EditRole && checkIndex(index)) { + Shared::VCard::Phone& item = deque[index.row()]; + switch (index.column()) { + case 0: + item.number = value.toString(); + return true; + case 1: { + quint8 newRole = value.toUInt(); + if (newRole > Shared::VCard::Phone::work) { + return false; + } + item.role = static_cast(newRole); + return true; + } + case 2: { + quint8 newType = value.toUInt(); + if (newType > Shared::VCard::Phone::other) { + return false; + } + item.type = static_cast(newType); + return true; + } + case 3: { + bool newDef = value.toBool(); + if (newDef != item.prefered) { + if (newDef) { + //dropPrefered(); + } + item.prefered = newDef; + return true; + } + } + } + return true; + } + return false; +} + +void UI::VCard::PhonesModel::setPhones(const std::deque& phones) +{ + if (deque.size() > 0) { + removeLines(0, deque.size()); + } + + if (phones.size() > 0) { + beginInsertRows(QModelIndex(), 0, phones.size() - 1); + for (const Shared::VCard::Phone& comming : phones) { + deque.emplace_back(comming); + } + endInsertRows(); + } +} + +QString UI::VCard::PhonesModel::getPhone(int row) const +{ + return deque[row].number; +} diff --git a/ui/widgets/vcard/phonesmodel.h b/ui/widgets/vcard/phonesmodel.h new file mode 100644 index 0000000..bf847d2 --- /dev/null +++ b/ui/widgets/vcard/phonesmodel.h @@ -0,0 +1,65 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#ifndef UI_VCARD_PHONESMODEL_H +#define UI_VCARD_PHONESMODEL_H + +#include +#include + +#include "global.h" + +namespace UI { +namespace VCard { + +/** + * @todo write docs + */ +class PhonesModel : public QAbstractTableModel +{ + Q_OBJECT +public: + PhonesModel(bool edit = false, QObject *parent = nullptr); + + QVariant data(const QModelIndex& index, int role) const override; + int columnCount(const QModelIndex& parent) const override; + int rowCount(const QModelIndex& parent) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool isPreferred(int row) const; + + void removeLines(int index, int count); + void setPhones(const std::deque& phones); + void getPhones(std::deque& phones) const; + QString getPhone(int row) const; + +public slots: + QModelIndex addNewEmptyLine(); + void revertPreferred(int row); + +private: + bool edit; + std::deque deque; + +private: + bool dropPrefered(); +}; + +}} + +#endif // UI_VCARD_PHONESMODEL_H diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp index 850830d..d66d6cd 100644 --- a/ui/widgets/vcard/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -38,7 +38,9 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): overlay(new QWidget()), contextMenu(new QMenu()), emails(edit), - roleDelegate(new ComboboxDelegate()) + phones(edit), + roleDelegate(new ComboboxDelegate()), + phoneTypeDelegate(new ComboboxDelegate()) { m_ui->setupUi(this); m_ui->jabberID->setText(jid); @@ -57,13 +59,30 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[1].toStdString().c_str())); roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[2].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[0].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[1].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[2].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[3].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[4].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[5].toStdString().c_str())); + phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[6].toStdString().c_str())); + m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); m_ui->emailsView->setModel(&emails); m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); - m_ui->emailsView->setColumnWidth(2, 30); + m_ui->emailsView->setColumnWidth(2, 25); m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->phonesView->setModel(&phones); + m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); + m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); + m_ui->phonesView->setColumnWidth(3, 25); + m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); + m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); if (edit) { @@ -121,6 +140,7 @@ VCard::~VCard() avatarMenu->deleteLater(); } + phoneTypeDelegate->deleteLater(); roleDelegate->deleteLater(); contextMenu->deleteLater(); } @@ -154,7 +174,9 @@ void VCard::setVCard(const Shared::VCard& card) updateAvatar(); const std::deque& ems = card.getEmails(); + const std::deque& phs = card.getPhones(); emails.setEmails(ems); + phones.setPhones(phs); } QString VCard::getJid() const @@ -181,6 +203,7 @@ void VCard::onButtonBoxAccepted() card.setAvatarType(currentAvatarType); emails.getEmails(card.getEmails()); + phones.getPhones(card.getPhones()); emit saveVCard(card); } @@ -282,8 +305,8 @@ void VCard::onContextMenu(const QPoint& point) bool hasMenu = false; QAbstractItemView* snd = static_cast(sender()); if (snd == m_ui->emailsView) { + hasMenu = true; if (editable) { - hasMenu = true; QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); connect(add, &QAction::triggered, this, &VCard::onAddEmail); @@ -306,6 +329,37 @@ void VCard::onContextMenu(const QPoint& point) connect(del, &QAction::triggered, this, &VCard::onRemoveEmail); } } + + QAction* cp = contextMenu->addAction(Shared::icon("copy"), tr("Copy selected emails to clipboard")); + connect(cp, &QAction::triggered, this, &VCard::onCopyEmail); + } else if (snd == m_ui->phonesView) { + hasMenu = true; + if (editable) { + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number")); + connect(add, &QAction::triggered, this, &VCard::onAddPhone); + + QItemSelectionModel* sm = m_ui->phonesView->selectionModel(); + int selectionSize = sm->selectedRows().size(); + + if (selectionSize > 0) { + if (selectionSize == 1) { + int row = sm->selectedRows().at(0).row(); + if (phones.isPreferred(row)) { + QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row)); + } else { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred")); + connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row)); + } + } + + QAction* del = contextMenu->addAction(Shared::icon("remove"), tr("Remove selected phone numbers")); + connect(del, &QAction::triggered, this, &VCard::onRemovePhone); + } + } + + QAction* cp = contextMenu->addAction(Shared::icon("copy"), tr("Copy selected phones to clipboard")); + connect(cp, &QAction::triggered, this, &VCard::onCopyPhone); } if (hasMenu) { @@ -326,6 +380,9 @@ void VCard::onAddAddress() } void VCard::onAddPhone() { + QModelIndex index = phones.addNewEmptyLine(); + m_ui->phonesView->setCurrentIndex(index); + m_ui->phonesView->edit(index); } void VCard::onRemoveAddress() { @@ -353,4 +410,53 @@ void VCard::onRemoveEmail() void VCard::onRemovePhone() { + QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); + + QList rows; + for (const QModelIndex& index : selection.indexes()) { + rows.append(index.row()); + } + + std::sort(rows.begin(), rows.end()); + + int prev = -1; + for (int i = rows.count() - 1; i >= 0; i -= 1) { + int current = rows[i]; + if (current != prev) { + phones.removeLines(current, 1); + prev = current; + } + } +} + +void VCard::onCopyEmail() +{ + QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); + + QList addrs; + for (const QModelIndex& index : selection.indexes()) { + addrs.push_back(emails.getEmail(index.row())); + } + + QString list = addrs.join("\n"); + + qDebug() << list; + QClipboard* cb = QApplication::clipboard(); + cb->setText(list); +} + +void VCard::onCopyPhone() +{ + QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); + + QList phs; + for (const QModelIndex& index : selection.indexes()) { + phs.push_back(phones.getPhone(index.row())); + } + + QString list = phs.join("\n"); + + qDebug() << list; + QClipboard* cb = QApplication::clipboard(); + cb->setText(list); } diff --git a/ui/widgets/vcard/vcard.h b/ui/widgets/vcard/vcard.h index e150ae9..8346fc3 100644 --- a/ui/widgets/vcard/vcard.h +++ b/ui/widgets/vcard/vcard.h @@ -31,11 +31,14 @@ #include #include #include +#include +#include #include #include "global.h" #include "emailsmodel.h" +#include "phonesmodel.h" #include "ui/utils/progress.h" #include "ui/utils/comboboxdelegate.h" @@ -71,8 +74,10 @@ private slots: void onAddAddress(); void onRemoveAddress(); void onAddEmail(); + void onCopyEmail(); void onRemoveEmail(); void onAddPhone(); + void onCopyPhone(); void onRemovePhone(); void onContextMenu(const QPoint& point); @@ -88,7 +93,9 @@ private: QWidget* overlay; QMenu* contextMenu; UI::VCard::EMailsModel emails; + UI::VCard::PhonesModel phones; ComboboxDelegate* roleDelegate; + ComboboxDelegate* phoneTypeDelegate; static const std::set supportedTypes; From dfa4d10c36d3180bc72bde24db06ea543cfff21a Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 7 Nov 2019 14:17:46 +0300 Subject: [PATCH 20/20] some VCard polishing, missing icons and translations --- core/adapterFuctions.cpp | 231 +++++----- global.h | 6 +- resources/images/fallback/dark/big/add.svg | 14 + resources/images/fallback/dark/big/copy.svg | 14 + .../images/fallback/dark/big/favorite.svg | 14 + .../images/fallback/dark/big/unfavorite.svg | 14 + resources/images/fallback/dark/small/add.svg | 13 + resources/images/fallback/dark/small/copy.svg | 13 + .../images/fallback/dark/small/favorite.svg | 14 + .../images/fallback/dark/small/unfavorite.svg | 14 + resources/images/fallback/light/big/add.svg | 14 + resources/images/fallback/light/big/copy.svg | 14 + .../images/fallback/light/big/favorite.svg | 14 + .../images/fallback/light/big/unfavorite.svg | 14 + resources/images/fallback/light/small/add.svg | 13 + .../images/fallback/light/small/copy.svg | 13 + .../images/fallback/light/small/favorite.svg | 14 + .../fallback/light/small/unfavorite.svg | 14 + resources/resources.qrc | 16 + translations/squawk.ru.ts | 415 +++++++++++++----- ui/widgets/vcard/emailsmodel.cpp | 2 +- ui/widgets/vcard/phonesmodel.cpp | 4 +- ui/widgets/vcard/vcard.cpp | 40 +- ui/widgets/vcard/vcard.ui | 40 +- 24 files changed, 727 insertions(+), 247 deletions(-) create mode 100644 resources/images/fallback/dark/big/add.svg create mode 100644 resources/images/fallback/dark/big/copy.svg create mode 100644 resources/images/fallback/dark/big/favorite.svg create mode 100644 resources/images/fallback/dark/big/unfavorite.svg create mode 100644 resources/images/fallback/dark/small/add.svg create mode 100644 resources/images/fallback/dark/small/copy.svg create mode 100644 resources/images/fallback/dark/small/favorite.svg create mode 100644 resources/images/fallback/dark/small/unfavorite.svg create mode 100644 resources/images/fallback/light/big/add.svg create mode 100644 resources/images/fallback/light/big/copy.svg create mode 100644 resources/images/fallback/light/big/favorite.svg create mode 100644 resources/images/fallback/light/big/unfavorite.svg create mode 100644 resources/images/fallback/light/small/add.svg create mode 100644 resources/images/fallback/light/small/copy.svg create mode 100644 resources/images/fallback/light/small/favorite.svg create mode 100644 resources/images/fallback/light/small/unfavorite.svg diff --git a/core/adapterFuctions.cpp b/core/adapterFuctions.cpp index 0279533..e2559d8 100644 --- a/core/adapterFuctions.cpp +++ b/core/adapterFuctions.cpp @@ -39,22 +39,25 @@ void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card) QList emails = card.emails(); std::deque& myEmails = vCard.getEmails(); for (const QXmppVCardEmail& em : emails) { - QXmppVCardEmail::Type et = em.type(); - bool prefered = false; - bool accounted = false; - if (et & QXmppVCardEmail::Preferred) { - prefered = true; - } - if (et & QXmppVCardEmail::Home) { - myEmails.emplace_back(em.address(), Shared::VCard::Email::home, prefered); - accounted = true; - } - if (et & QXmppVCardEmail::Work) { - myEmails.emplace_back(em.address(), Shared::VCard::Email::work, prefered); - accounted = true; - } - if (!accounted) { - myEmails.emplace_back(em.address(), Shared::VCard::Email::none, prefered); + QString addr = em.address(); + if (addr.size() != 0) { + QXmppVCardEmail::Type et = em.type(); + bool prefered = false; + bool accounted = false; + if (et & QXmppVCardEmail::Preferred) { + prefered = true; + } + if (et & QXmppVCardEmail::Home) { + myEmails.emplace_back(addr, Shared::VCard::Email::home, prefered); + accounted = true; + } + if (et & QXmppVCardEmail::Work) { + myEmails.emplace_back(addr, Shared::VCard::Email::work, prefered); + accounted = true; + } + if (!accounted) { + myEmails.emplace_back(addr, Shared::VCard::Email::none, prefered); + } } } @@ -62,113 +65,115 @@ void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card) QList phones = card.phones(); std::deque& myPhones = vCard.getPhones(); for (const QXmppVCardPhone& ph : phones) { - Shared::VCard::Phone mPh(ph.number()); - QXmppVCardPhone::Type pt = ph.type(); - bool prefered = false; - bool accounted = false; - if (pt & QXmppVCardPhone::Preferred) { - prefered = true; - } - - bool home = false; - bool work = false; - - if (pt & QXmppVCardPhone::Home) { - home = true; - } - if (pt & QXmppVCardPhone::Work) { - work = true; - } - - - if (pt & QXmppVCardPhone::Fax) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::fax, Shared::VCard::Phone::home, prefered); - } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::fax, Shared::VCard::Phone::work, prefered); - } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::fax, Shared::VCard::Phone::none, prefered); + QString num = ph.number(); + if (num.size() != 0) { + QXmppVCardPhone::Type pt = ph.type(); + bool prefered = false; + bool accounted = false; + if (pt & QXmppVCardPhone::Preferred) { + prefered = true; } - accounted = true; - } - if (pt & QXmppVCardPhone::Voice) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::voice, Shared::VCard::Phone::home, prefered); - } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::voice, Shared::VCard::Phone::work, prefered); - } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::voice, Shared::VCard::Phone::none, prefered); + + bool home = false; + bool work = false; + + if (pt & QXmppVCardPhone::Home) { + home = true; } - accounted = true; - } - if (pt & QXmppVCardPhone::Pager) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::pager, Shared::VCard::Phone::home, prefered); - } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::pager, Shared::VCard::Phone::work, prefered); - } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::pager, Shared::VCard::Phone::none, prefered); + if (pt & QXmppVCardPhone::Work) { + work = true; } - accounted = true; - } - if (pt & QXmppVCardPhone::Cell) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::cell, Shared::VCard::Phone::home, prefered); + + + if (pt & QXmppVCardPhone::Fax) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::none, prefered); } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::cell, Shared::VCard::Phone::work, prefered); + accounted = true; + } + if (pt & QXmppVCardPhone::Voice) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::none, prefered); } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::cell, Shared::VCard::Phone::none, prefered); - } - accounted = true; - } - if (pt & QXmppVCardPhone::Video) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::video, Shared::VCard::Phone::home, prefered); + accounted = true; + } + if (pt & QXmppVCardPhone::Pager) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::none, prefered); } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::video, Shared::VCard::Phone::work, prefered); + accounted = true; + } + if (pt & QXmppVCardPhone::Cell) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::none, prefered); } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::video, Shared::VCard::Phone::none, prefered); - } - accounted = true; - } - if (pt & QXmppVCardPhone::Modem) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::modem, Shared::VCard::Phone::home, prefered); + accounted = true; + } + if (pt & QXmppVCardPhone::Video) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::none, prefered); } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::modem, Shared::VCard::Phone::work, prefered); + accounted = true; + } + if (pt & QXmppVCardPhone::Modem) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::none, prefered); } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::modem, Shared::VCard::Phone::none, prefered); - } - accounted = true; - } - if (!accounted) { - if (home || work) { - if (home) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::other, Shared::VCard::Phone::home, prefered); + accounted = true; + } + if (!accounted) { + if (home || work) { + if (home) { + myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::home, prefered); + } + if (work) { + myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::work, prefered); + } + } else { + myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::none, prefered); } - if (work) { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::other, Shared::VCard::Phone::work, prefered); - } - } else { - myPhones.emplace_back(ph.number(), Shared::VCard::Phone::other, Shared::VCard::Phone::none, prefered); } } } diff --git a/global.h b/global.h index ccb7317..d5d206e 100644 --- a/global.h +++ b/global.h @@ -457,7 +457,9 @@ static const std::map> icons = { {"state-ok", {"state-ok", "state-ok"}}, {"state-error", {"state-error", "state-error"}}, + {"edit-copy", {"edit-copy", "copy"}}, {"edit-delete", {"edit-delete", "edit-delete"}}, + {"edit-rename", {"edit-rename", "edit-rename"}}, {"mail-message", {"mail-message", "mail-message"}}, {"mail-attachment", {"mail-attachment", "mail-attachment"}}, {"network-connect", {"network-connect", "network-connect"}}, @@ -469,9 +471,11 @@ static const std::map> icons = { {"clean", {"edit-clear-all", "clean"}}, {"user", {"user", "user"}}, {"user-properties", {"user-properties", "user-properties"}}, - {"edit-rename", {"edit-rename", "edit-rename"}}, {"group", {"group", "group"}}, {"group-new", {"resurce-group-new", "group-new"}}, + {"favorite", {"favorite", "favorite"}}, + {"unfavorite", {"draw-star", "unfavorite"}}, + {"list-add", {"list-add", "add"}}, }; }; diff --git a/resources/images/fallback/dark/big/add.svg b/resources/images/fallback/dark/big/add.svg new file mode 100644 index 0000000..f9bae3c --- /dev/null +++ b/resources/images/fallback/dark/big/add.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/copy.svg b/resources/images/fallback/dark/big/copy.svg new file mode 100644 index 0000000..7470e5f --- /dev/null +++ b/resources/images/fallback/dark/big/copy.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/favorite.svg b/resources/images/fallback/dark/big/favorite.svg new file mode 100644 index 0000000..5751db6 --- /dev/null +++ b/resources/images/fallback/dark/big/favorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/unfavorite.svg b/resources/images/fallback/dark/big/unfavorite.svg new file mode 100644 index 0000000..3d128ea --- /dev/null +++ b/resources/images/fallback/dark/big/unfavorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/add.svg b/resources/images/fallback/dark/small/add.svg new file mode 100644 index 0000000..8779062 --- /dev/null +++ b/resources/images/fallback/dark/small/add.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/copy.svg b/resources/images/fallback/dark/small/copy.svg new file mode 100644 index 0000000..061d734 --- /dev/null +++ b/resources/images/fallback/dark/small/copy.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/favorite.svg b/resources/images/fallback/dark/small/favorite.svg new file mode 100644 index 0000000..5751db6 --- /dev/null +++ b/resources/images/fallback/dark/small/favorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/unfavorite.svg b/resources/images/fallback/dark/small/unfavorite.svg new file mode 100644 index 0000000..3d128ea --- /dev/null +++ b/resources/images/fallback/dark/small/unfavorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/add.svg b/resources/images/fallback/light/big/add.svg new file mode 100644 index 0000000..0d6166a --- /dev/null +++ b/resources/images/fallback/light/big/add.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/copy.svg b/resources/images/fallback/light/big/copy.svg new file mode 100644 index 0000000..427de29 --- /dev/null +++ b/resources/images/fallback/light/big/copy.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/favorite.svg b/resources/images/fallback/light/big/favorite.svg new file mode 100644 index 0000000..f6025c6 --- /dev/null +++ b/resources/images/fallback/light/big/favorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/unfavorite.svg b/resources/images/fallback/light/big/unfavorite.svg new file mode 100644 index 0000000..5eef7a3 --- /dev/null +++ b/resources/images/fallback/light/big/unfavorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/small/add.svg b/resources/images/fallback/light/small/add.svg new file mode 100644 index 0000000..f05db06 --- /dev/null +++ b/resources/images/fallback/light/small/add.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/copy.svg b/resources/images/fallback/light/small/copy.svg new file mode 100644 index 0000000..c557cef --- /dev/null +++ b/resources/images/fallback/light/small/copy.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/favorite.svg b/resources/images/fallback/light/small/favorite.svg new file mode 100644 index 0000000..f6025c6 --- /dev/null +++ b/resources/images/fallback/light/small/favorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/small/unfavorite.svg b/resources/images/fallback/light/small/unfavorite.svg new file mode 100644 index 0000000..5eef7a3 --- /dev/null +++ b/resources/images/fallback/light/small/unfavorite.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/resources.qrc b/resources/resources.qrc index 3cfaa84..4fb3e5b 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -36,6 +36,10 @@ images/fallback/dark/big/group-new.svg images/fallback/dark/big/edit-rename.svg images/fallback/dark/big/user-properties.svg + images/fallback/dark/big/copy.svg + images/fallback/dark/big/favorite.svg + images/fallback/dark/big/unfavorite.svg + images/fallback/dark/big/add.svg images/fallback/dark/small/absent.svg @@ -72,6 +76,10 @@ images/fallback/dark/small/group-new.svg images/fallback/dark/small/edit-rename.svg images/fallback/dark/small/user-properties.svg + images/fallback/dark/small/copy.svg + images/fallback/dark/small/favorite.svg + images/fallback/dark/small/unfavorite.svg + images/fallback/dark/small/add.svg images/fallback/light/big/absent.svg @@ -108,6 +116,10 @@ images/fallback/light/big/group-new.svg images/fallback/light/big/edit-rename.svg images/fallback/light/big/user-properties.svg + images/fallback/light/big/copy.svg + images/fallback/light/big/favorite.svg + images/fallback/light/big/unfavorite.svg + images/fallback/light/big/add.svg images/fallback/light/small/absent.svg @@ -144,5 +156,9 @@ images/fallback/light/small/group-new.svg images/fallback/light/small/edit-rename.svg images/fallback/light/small/user-properties.svg + images/fallback/light/small/copy.svg + images/fallback/light/small/favorite.svg + images/fallback/light/small/unfavorite.svg + images/fallback/light/small/add.svg diff --git a/translations/squawk.ru.ts b/translations/squawk.ru.ts index bb1fdef..e7bae69 100644 --- a/translations/squawk.ru.ts +++ b/translations/squawk.ru.ts @@ -5,92 +5,77 @@ Account - Account Заголовок окна Учетная запись - Your account login Имя пользователя Вашей учетной записи - john_smith1987 ivan_ivanov1987 - Server Сервер - A server address of your account. Like 404.city or macaw.me Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me) - macaw.me macaw.me - Login Имя учетной записи - Password Пароль - Password of your account Пароль вашей учетной записи - Name Имя - Just a name how would you call this account, doesn't affect anything Просто имя, то как Вы называете свою учетную запись, может быть любым - John Иван - Resource Ресурс - A resource name like "Home" or "Work" Имя этой программы для ваших контактов, может быть "Home" или "Phone" - QXmpp Ресурс по умолчанию QXmpp @@ -100,44 +85,38 @@ Accounts - Accounts Учетные записи - Delete Удалить - Add Добавить - Edit Редактировать - Change password Изменить пароль - - - + + Connect Подключить - + Disconnect Отключить @@ -146,10 +125,14 @@ Conversation - Type your message here... Введите сообщение... + + + Chose a file to send + Выберите файл для отправки + Global @@ -253,67 +236,97 @@ Moderator Модератор + + Not specified + Не указан + + + Personal + Личный + + + Business + Рабочий + + + Fax + Факс + + + Pager + Пэйджер + + + Voice + Стационарный + + + Cell + Мобильный + + + Video + Видеофон + + + Modem + Модем + + + Other + Другой + JoinConference - Join new conference Заголовок окна Присоединиться к новой беседе - JID JID - Room JID Jabber-идентификатор беседы - identifier@conference.server.org identifier@conference.server.org - Account Учетная запись - Join on login Автовход - If checked Squawk will try to join this conference on login Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении - Nick name Псевдоним - Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи - John Ivan @@ -321,24 +334,24 @@ Message - + Download Скачать - + Error downloading file: %1 You can try again Ошибка загрузки файла: %1 Вы можете попробовать снова - + %1 is offering you to download a file %1 предлагает Вам скачать файл - + Open Открыть @@ -388,67 +401,67 @@ You can try again Models::Roster - + New messages Есть непрочитанные сообщения - - - + + + New messages: Новых сообщений: - + Jabber ID: Идентификатор: - - - + + + Availability: Доступность: - - - + + + Status: Статус: - - - + + + Subscription: Подписка: - + Affiliation: Я правда не знаю, как это объяснить, не то что перевести Причастность: - + Role: Роль: - + Online contacts: Контакстов в сети: - + Total contacts: Всего контактов: - + Members: Участников: @@ -457,57 +470,48 @@ You can try again NewContact - Add new contact Заголовок окна Добавление нового контакта - Account Учетная запись - An account that is going to have new contact Учетная запись для которой будет добавлен контакт - JID JID - Jabber id of your new contact Jabber-идентификатор нового контакта - name@server.dmn Placeholder поля ввода JID name@server.dmn - Name Имя - The way this new contact will be labeled in your roster (optional) То, как будет подписан контакт в вашем списке контактов (не обязательно) - John Smith Иван Иванов @@ -516,87 +520,91 @@ You can try again Squawk - squawk Squawk - - + Settings Настройки - - + Squawk Squawk - - + Accounts Учетные записи - - + Quit Выйти - - + Add contact Добавить контакт - - + Add conference Присоединиться к беседе - + + Contact list + Список контактов + + + Disconnect Отключить - + Connect Подключить - - - + + + VCard + Карточка + + + + + Remove Удалить - + Open dialog Открыть диалог - - + + Unsubscribe Отписаться - - + + Subscribe Подписаться - + Rename Переименовать - + Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -606,34 +614,245 @@ to be displayed as %1 %1 - + Renaming %1 Назначение имени контакту %1 - + Groups Группы - + New group Создать новую группу - + New group name Имя группы - + Add %1 to a new group Добавление %1 в новую группу - + Open conversation Открыть окно беседы + + + %1 account card + Карточка учетной записи %1 + + + + %1 contact card + Карточка контакта %1 + + + + Downloading vCard + Получение карточки + + + + VCard + + + Received 12.07.2007 at 17.35 + Не обновлялось + + + + + General + Общее + + + + Organization + Место работы + + + + Middle name + Среднее имя + + + + First name + Имя + + + + Last name + Фамилия + + + + Nick name + Псевдоним + + + + Birthday + Дата рождения + + + + Organization name + Название организации + + + + Unit / Department + Отдел + + + + Role / Profession + Профессия + + + + Job title + Наименование должности + + + + Full name + Полное имя + + + + Personal information + Личная информация + + + + Addresses + Адреса + + + + E-Mail addresses + Адреса электронной почты + + + + Phone numbers + Номера телефонов + + + + + Contact + Контактная информация + + + + Jabber ID + Jabber ID + + + + Web site + Веб сайт + + + + + Description + Описание + + + + Set avatar + Установить иконку + + + + Clear avatar + Убрать иконку + + + + Account %1 card + Карточка учетной записи %1 + + + + Contact %1 card + Карточка контакта %1 + + + + Received %1 at %2 + Получено %1 в %2 + + + + Chose your new avatar + Выберите новую иконку + + + + Images (*.png *.jpg *.jpeg) + Изображения (*.png *.jpg *.jpeg) + + + + Add email address + Добавить адрес электронной почты + + + + Unset this email as preferred + Убрать отметку "предпочтительный" с этого адреса + + + + Set this email as preferred + Отметить этот адрес как "предпочтительный" + + + + Remove selected email addresses + Удалить выбранные адреса + + + + Copy selected emails to clipboard + Скопировать выбранные адреса в буфер обмена + + + + Add phone number + Добавить номер телефона + + + + Unset this phone as preferred + Убрать отметку "предпочтительный" с этого номера + + + + Set this phone as preferred + Отметить этот номер как "предпочтительный" + + + + Remove selected phone numbers + Удалить выбранные телефонные номера + + + + Copy selected phones to clipboard + Скопировать выбранные телефонные номера в буфер обмена + diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/widgets/vcard/emailsmodel.cpp index 18838ee..4044322 100644 --- a/ui/widgets/vcard/emailsmodel.cpp +++ b/ui/widgets/vcard/emailsmodel.cpp @@ -52,7 +52,7 @@ QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const case 1: switch (role) { case Qt::DisplayRole: - return tr(Shared::VCard::Email::roleNames[deque[index.row()].role].toStdString().c_str()); + return QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[deque[index.row()].role].toStdString().c_str()); case Qt::EditRole: return deque[index.row()].role; default: diff --git a/ui/widgets/vcard/phonesmodel.cpp b/ui/widgets/vcard/phonesmodel.cpp index 4371dff..df9cad6 100644 --- a/ui/widgets/vcard/phonesmodel.cpp +++ b/ui/widgets/vcard/phonesmodel.cpp @@ -52,7 +52,7 @@ QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const case 1: switch (role) { case Qt::DisplayRole: - return tr(Shared::VCard::Phone::roleNames[deque[index.row()].role].toStdString().c_str()); + return QCoreApplication::translate("Global", Shared::VCard::Phone::roleNames[deque[index.row()].role].toStdString().c_str()); case Qt::EditRole: return deque[index.row()].role; default: @@ -62,7 +62,7 @@ QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const case 2: switch (role) { case Qt::DisplayRole: - return tr(Shared::VCard::Phone::typeNames[deque[index.row()].type].toStdString().c_str()); + return QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[deque[index.row()].type].toStdString().c_str()); case Qt::EditRole: return deque[index.row()].type; default: diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp index d66d6cd..44b5aa3 100644 --- a/ui/widgets/vcard/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -55,17 +55,17 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): setAvatar->setEnabled(true); clearAvatar->setEnabled(false); - roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[0].toStdString().c_str())); - roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[1].toStdString().c_str())); - roleDelegate->addEntry(tr(Shared::VCard::Email::roleNames[2].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[0].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[1].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[2].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[3].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[4].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[5].toStdString().c_str())); - phoneTypeDelegate->addEntry(tr(Shared::VCard::Phone::typeNames[6].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); m_ui->emailsView->setModel(&emails); @@ -317,7 +317,7 @@ void VCard::onContextMenu(const QPoint& point) if (selectionSize == 1) { int row = sm->selectedRows().at(0).row(); if (emails.isPreferred(row)) { - QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this email as preferred")); + QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred")); connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); } else { QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); @@ -325,12 +325,12 @@ void VCard::onContextMenu(const QPoint& point) } } - QAction* del = contextMenu->addAction(Shared::icon("remove"), tr("Remove selected email addresses")); + QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected email addresses")); connect(del, &QAction::triggered, this, &VCard::onRemoveEmail); } } - QAction* cp = contextMenu->addAction(Shared::icon("copy"), tr("Copy selected emails to clipboard")); + QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected emails to clipboard")); connect(cp, &QAction::triggered, this, &VCard::onCopyEmail); } else if (snd == m_ui->phonesView) { hasMenu = true; @@ -353,12 +353,12 @@ void VCard::onContextMenu(const QPoint& point) } } - QAction* del = contextMenu->addAction(Shared::icon("remove"), tr("Remove selected phone numbers")); + QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected phone numbers")); connect(del, &QAction::triggered, this, &VCard::onRemovePhone); } } - QAction* cp = contextMenu->addAction(Shared::icon("copy"), tr("Copy selected phones to clipboard")); + QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected phones to clipboard")); connect(cp, &QAction::triggered, this, &VCard::onCopyPhone); } @@ -431,32 +431,30 @@ void VCard::onRemovePhone() void VCard::onCopyEmail() { - QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); + QList selection(m_ui->emailsView->selectionModel()->selectedRows()); QList addrs; - for (const QModelIndex& index : selection.indexes()) { + for (const QModelIndex& index : selection) { addrs.push_back(emails.getEmail(index.row())); } QString list = addrs.join("\n"); - qDebug() << list; QClipboard* cb = QApplication::clipboard(); cb->setText(list); } void VCard::onCopyPhone() { - QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); + QList selection(m_ui->phonesView->selectionModel()->selectedRows()); QList phs; - for (const QModelIndex& index : selection.indexes()) { + for (const QModelIndex& index : selection) { phs.push_back(phones.getPhone(index.row())); } QString list = phs.join("\n"); - qDebug() << list; QClipboard* cb = QApplication::clipboard(); cb->setText(list); } diff --git a/ui/widgets/vcard/vcard.ui b/ui/widgets/vcard/vcard.ui index e08bf49..26db8f9 100644 --- a/ui/widgets/vcard/vcard.ui +++ b/ui/widgets/vcard/vcard.ui @@ -124,10 +124,10 @@ - + font: 600 16pt; - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Organization</span></p></body></html> + Organization Qt::AlignCenter @@ -418,8 +418,11 @@ + + font: 600 24pt ; + - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">General</span></p></body></html> + General @@ -445,7 +448,7 @@ - + font: 600 16pt; QFrame::NoFrame @@ -454,7 +457,7 @@ QFrame::Plain - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Personal information</span></p></body></html> + Personal information Qt::AlignCenter @@ -536,8 +539,11 @@ + + font: 600 24pt ; + - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Contact</span></p></body></html> + Contact @@ -561,7 +567,7 @@ 0 0 566 - 497 + 498 @@ -640,8 +646,11 @@ + + font: 600 16pt; + - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Addresses</span></p></body></html> + Addresses Qt::AlignCenter @@ -666,8 +675,11 @@ + + font: 600 16pt; + - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">E-Mail addresses</span></p></body></html> + E-Mail addresses Qt::AlignCenter @@ -748,8 +760,11 @@ + + font: 600 16pt; + - <html><head/><body><p><span style=" font-size:16pt; font-weight:600;">Phone numbers</span></p></body></html> + Phone numbers Qt::AlignCenter @@ -800,8 +815,11 @@ + + font: 600 24pt ; + - <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Description</span></p></body></html> + Description