From eda96e138d0938bff94288305e320f1617657763 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 11 Jul 2019 11:51:52 +0300 Subject: [PATCH] mucs initial, just display; archive fixes --- core/account.cpp | 128 ++++++++++++++++++++++++++++- core/account.h | 40 +++++++++ core/archive.cpp | 63 +++++++------- core/contact.cpp | 4 +- core/squawk.cpp | 25 ++++++ core/squawk.h | 6 ++ main.cpp | 6 ++ ui/CMakeLists.txt | 1 + ui/conversation.cpp | 2 +- ui/models/contact.cpp | 2 +- ui/models/contact.h | 2 +- ui/models/item.cpp | 9 +- ui/models/item.h | 2 +- ui/models/room.cpp | 186 ++++++++++++++++++++++++++++++++++++++++++ ui/models/room.h | 70 ++++++++++++++++ ui/models/roster.cpp | 100 +++++++++++++++++++++-- ui/models/roster.h | 5 ++ ui/squawk.cpp | 15 ++++ ui/squawk.h | 3 + 19 files changed, 626 insertions(+), 43 deletions(-) create mode 100644 ui/models/room.cpp create mode 100644 ui/models/room.h diff --git a/core/account.cpp b/core/account.cpp index 5277b40..cf38b49 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -16,11 +16,14 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& groups(), cm(new QXmppCarbonManager()), am(new QXmppMamManager()), + mm(new QXmppMucManager()), + bm(new QXmppBookmarkManager()), contacts(), maxReconnectTimes(0), reconnectTimes(0), queuedContacts(), - outOfRosterContacts() + outOfRosterContacts(), + mucInfo() { config.setUser(p_login); config.setDomain(p_server); @@ -52,6 +55,12 @@ Account::Account(const QString& p_login, const QString& p_server, 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))); + + client.addExtension(mm); + QObject::connect(mm, SIGNAL(roomAdded(QXmppMucRoom*)), this, SLOT(onMucRoomAdded(QXmppMucRoom*))); + + client.addExtension(bm); + QObject::connect(bm, SIGNAL(bookmarksReceived(const QXmppBookmarkSet&)), this, SLOT(bookmarksReceived(const QXmppBookmarkSet&))); } Account::~Account() @@ -59,6 +68,11 @@ Account::~Account() for (std::map::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) { delete itr->second; } + + delete bm; + delete mm; + delete am; + delete cm; } Shared::ConnectionState Core::Account::getState() const @@ -433,6 +447,7 @@ void Core::Account::sendMessage(const Shared::Message& data) msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible std::map::const_iterator itr = contacts.find(data.getPenPalJid()); + itr->second->appendMessageToArchive(data); client.sendPacket(msg); @@ -846,3 +861,114 @@ 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 not in the connected state, skipping"; } } + +void Core::Account::onMucRoomAdded(QXmppMucRoom* room) +{ + qDebug() << "room" << room->jid() << "added with name" << room->name() << ", account" << getName() << "joined:" << room->isJoined(); +} + +void Core::Account::bookmarksReceived(const QXmppBookmarkSet& bookmarks) +{ + QList conferences = bookmarks.conferences(); + for (QList::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) { + const QXmppBookmarkConference& c = *itr; + + QString jid = c.jid(); + std::pair::iterator, bool> mi = mucInfo.insert(std::make_pair(jid, MucInfo(c.autoJoin(), false, jid, c.name(), c.nickName()))); + if (mi.second) { + const MucInfo& info = mi.first->second; + QXmppMucRoom* room = mm->addRoom(jid); + + QObject::connect(room, SIGNAL(joined()), this, SLOT(onMucJoined())); + QObject::connect(room, SIGNAL(left()), this, SLOT(onMucLeft())); + QObject::connect(room, SIGNAL(nameChanged(const QString&)), this, SLOT(onMucNameChanged(const QString&))); + QObject::connect(room, SIGNAL(nickNameChanged(const QString&)), this, SLOT(onMucNickNameChanged(const QString&))); + QObject::connect(room, SIGNAL(error(const QXmppStanza::Error&)), this, SLOT(onMucError(const QXmppStanza::Error&))); + QObject::connect(room, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onMucMessage(const QXmppMessage&))); + + emit addRoom(jid, { + {"autoJoin", info.autoJoin}, + {"joined", false}, + {"nick", info.nick}, + {"name", info.name} + }); + + room->setNickName(info.nick == "" ? getName() : info.nick); + if (info.autoJoin) { + room->join(); + } + } else { + qDebug() << "Received a bookmark to a MUC " << jid << " which is already booked by another bookmark, skipping"; + } + } +} + +void Core::Account::onMucJoined() +{ + QXmppMucRoom* room = static_cast(sender()); + QString jid = room->jid(); + std::map::iterator itr = mucInfo.find(jid); + if (itr != mucInfo.end()) { + itr->second.joined = true; + emit changeRoom(jid, { + {"joined", true} + }); + } else { + qDebug() << "Seems like account" << getName() << "joined room" << jid << ", but the info about that room wasn't found, skipping"; + } +} + +void Core::Account::onMucLeft() +{ + QXmppMucRoom* room = static_cast(sender()); + QString jid = room->jid(); + std::map::iterator itr = mucInfo.find(jid); + if (itr != mucInfo.end()) { + itr->second.joined = false; + emit changeRoom(jid, { + {"joined", false} + }); + } else { + qDebug() << "Seems like account" << getName() << "left room" << jid << ", but the info about that room wasn't found, skipping"; + } +} + +void Core::Account::onMucNameChanged(const QString& roomName) +{ + QXmppMucRoom* room = static_cast(sender()); + QString jid = room->jid(); + std::map::iterator itr = mucInfo.find(jid); + if (itr != mucInfo.end()) { + itr->second.name = roomName; + emit changeRoom(jid, { + {"name", roomName} + }); + } else { + qDebug() << "Account" << getName() << "received an event about room" << jid << "name change, but the info about that room wasn't found, skipping"; + } +} + +void Core::Account::onMucNickNameChanged(const QString& nickName) +{ + QXmppMucRoom* room = static_cast(sender()); + QString jid = room->jid(); + std::map::iterator itr = mucInfo.find(jid); + if (itr != mucInfo.end()) { + itr->second.nick = nickName; + emit changeRoom(jid, { + {"nick", nickName} + }); + } else { + qDebug() << "Account" << getName() << "received an event about his nick name change in room" << jid << ", but the info about that room wasn't found, skipping"; + } +} + +void Core::Account::onMucError(const QXmppStanza::Error& error) +{ + qDebug() << "MUC error"; +} + +void Core::Account::onMucMessage(const QXmppMessage& message) +{ + qDebug() << "Muc message"; +} diff --git a/core/account.h b/core/account.h index 180c234..14377be 100644 --- a/core/account.h +++ b/core/account.h @@ -8,7 +8,10 @@ #include #include #include +#include #include +#include +#include #include "../global.h" #include "contact.h" @@ -18,6 +21,7 @@ namespace Core class Account : public QObject { Q_OBJECT + class MucInfo; public: Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent = 0); ~Account(); @@ -54,6 +58,9 @@ signals: void availabilityChanged(int); void addGroup(const QString& name); void removeGroup(const QString& name); + void addRoom(const QString& jid, const QMap& data); + void changeRoom(const QString& jid, const QMap& data); + void removeRoom(const QString& jid); void addContact(const QString& jid, const QString& group, const QMap& data); void removeContact(const QString& jid); void removeContact(const QString& jid, const QString& group); @@ -74,12 +81,15 @@ private: std::map> groups; QXmppCarbonManager* cm; QXmppMamManager* am; + QXmppMucManager* mm; + QXmppBookmarkManager* bm; std::map contacts; unsigned int maxReconnectTimes; unsigned int reconnectTimes; std::map queuedContacts; std::set outOfRosterContacts; + std::map mucInfo; private slots: void onClientConnected(); @@ -101,6 +111,16 @@ private slots: void onMamMessageReceived(const QString& bareJid, const QXmppMessage& message); void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); + void onMucRoomAdded(QXmppMucRoom* room); + void onMucJoined(); + void onMucLeft(); + void onMucNameChanged(const QString& roomName); + void onMucNickNameChanged(const QString& nickName); + void onMucError(const QXmppStanza::Error& error); + void onMucMessage(const QXmppMessage& message); + + void bookmarksReceived(const QXmppBookmarkSet& bookmarks); + void onContactGroupAdded(const QString& group); void onContactGroupRemoved(const QString& group); void onContactNameChanged(const QString& name); @@ -119,8 +139,28 @@ private: void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const; Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs) const; void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); + + +class MucInfo { +public: + MucInfo(bool p_al, bool p_jo, const QString& p_jid, const QString& p_name, const QString& p_nick): + autoJoin(p_al), + joined(p_jo), + jid(p_jid), + name(p_name), + nick(p_nick), + removed(false) {} + + bool autoJoin; + bool joined; + QString jid; + QString name; + QString nick; + bool removed; +}; }; } + #endif // CORE_ACCOUNT_H diff --git a/core/archive.cpp b/core/archive.cpp index 21d1658..f705ac6 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -31,7 +31,8 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent): fromTheBeginning(false), environment(), main(), - order() + order(), + stats() { } @@ -55,14 +56,14 @@ void Core::Archive::open(const QString& account) } } - mdb_env_set_maxdbs(environment, 3); + mdb_env_set_maxdbs(environment, 4); mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); mdb_dbi_open(txn, "main", MDB_CREATE, &main); mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order); - mdb_dbi_open(txn, "order", MDB_CREATE, &stats); + mdb_dbi_open(txn, "stats", MDB_CREATE, &stats); mdb_txn_commit(txn); fromTheBeginning = _isFromTheBeginning(); opened = true; @@ -135,6 +136,7 @@ void Core::Archive::clear() mdb_txn_begin(environment, NULL, 0, &txn); mdb_drop(txn, main, 0); mdb_drop(txn, order, 0); + mdb_drop(txn, stats, 0); mdb_txn_commit(txn); } @@ -201,6 +203,32 @@ QString Core::Archive::newestId() } } +QString Core::Archive::oldestId() +{ + if (!opened) { + throw Closed("oldestId", jid.toStdString()); + } + MDB_txn *txn; + int rc; + rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); + MDB_cursor* cursor; + rc = mdb_cursor_open(txn, order, &cursor); + MDB_val lmdbKey, lmdbData; + + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); + if (rc) { + qDebug() << "Error geting oldestId " << mdb_strerror(rc); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + throw Empty(jid.toStdString()); + } else { + std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + return sId.c_str(); + } +} + Shared::Message Core::Archive::oldest() { return getElement(oldestId()); @@ -264,33 +292,6 @@ unsigned int Core::Archive::addElements(const std::list& messag return success; } - -QString Core::Archive::oldestId() -{ - if (!opened) { - throw Closed("oldestId", jid.toStdString()); - } - MDB_txn *txn; - int rc; - rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - MDB_cursor* cursor; - rc = mdb_cursor_open(txn, order, &cursor); - MDB_val lmdbKey, lmdbData; - - rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); - if (rc) { - qDebug() << "Error geting oldestId " << mdb_strerror(rc); - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - throw Empty(jid.toStdString()); - } else { - std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - return sId.c_str(); - } -} - long unsigned int Core::Archive::size() const { if (!opened) { @@ -422,6 +423,7 @@ bool Core::Archive::_isFromTheBeginning() } else { qDebug() <<"isFromTheBeginning error: stored value doesn't match any magic number, the answer is most probably wrong"; } + mdb_txn_abort(txn); return is; } } @@ -460,6 +462,7 @@ void Core::Archive::setFromTheBeginning(bool is) qDebug() << "Couldn't store beginning key into stat database:" << mdb_strerror(rc); mdb_txn_abort(txn); } + mdb_txn_commit(txn); } } diff --git a/core/contact.cpp b/core/contact.cpp index 6df230d..d5cf42c 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -266,6 +266,7 @@ void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId hisoryCache.clear(); } + bool wasEmpty = false; switch (archiveState) { case beginning: if (finished) { @@ -288,9 +289,10 @@ void Core::Contact::flushMessagesToArchive(bool finished, const QString& firstId } break; case empty: + wasEmpty = true; archiveState = end; case end: - if (finished) { + if (finished && (added > 0 || !wasEmpty)) { archiveState = complete; archive->setFromTheBeginning(true); } diff --git a/core/squawk.cpp b/core/squawk.cpp index d53a1c3..036eea2 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -102,6 +102,13 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const connect(acc, SIGNAL(responseArchive(const QString&, const std::list&)), this, SLOT(onAccountResponseArchive(const QString&, const std::list&))); + 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&))); + + QMap map = { {"login", login}, {"server", server}, @@ -376,3 +383,21 @@ void Core::Squawk::addContactRequest(const QString& account, const QString& jid, itr->second->addContactRequest(jid, name, groups); } + +void Core::Squawk::onAccountAddRoom(const QString jid, const QMap& data) +{ + Account* acc = static_cast(sender()); + emit addRoom(acc->getName(), jid, data); +} + +void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap& data) +{ + Account* acc = static_cast(sender()); + emit changeRoom(acc->getName(), jid, data); +} + +void Core::Squawk::onAccountRemoveRoom(const QString jid) +{ + Account* acc = static_cast(sender()); + emit removeRoom(acc->getName(), jid); +} diff --git a/core/squawk.h b/core/squawk.h index fa65fa8..7c9ee1f 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -37,6 +37,9 @@ signals: void stateChanged(int state); void accountMessage(const QString& account, const Shared::Message& data); void responseArchive(const QString& account, const QString& jid, const std::list& list); + void addRoom(const QString& account, const QString jid, const QMap& data); + void changeRoom(const QString& account, const QString jid, const QMap& data); + void removeRoom(const QString& account, const QString jid); public slots: void start(); @@ -79,6 +82,9 @@ private slots: void onAccountRemovePresence(const QString& jid, const QString& name); void onAccountMessage(const Shared::Message& data); void onAccountResponseArchive(const QString& jid, const std::list& list); + void onAccountAddRoom(const QString jid, const QMap& data); + void onAccountChangeRoom(const QString jid, const QMap& data); + void onAccountRemoveRoom(const QString jid); }; } diff --git a/main.cpp b/main.cpp index 5ab4156..99861c6 100644 --- a/main.cpp +++ b/main.cpp @@ -83,6 +83,12 @@ int main(int argc, char *argv[]) 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&))); + //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); coreThread->start(); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index bcdc838..cc8af38 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -21,6 +21,7 @@ set(squawkUI_SRC models/contact.cpp models/presence.cpp models/group.cpp + models/room.cpp conversation.cpp messageline.cpp newcontact.cpp diff --git a/ui/conversation.cpp b/ui/conversation.cpp index 52e98d8..41425f6 100644 --- a/ui/conversation.cpp +++ b/ui/conversation.cpp @@ -51,6 +51,7 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): //connect(m_ui->attachButton, SIGNAL(clicked(bool)), this, SLOT(onAttach())); m_ui->messageEditor->installEventFilter(&ker); + line->setMyName(p_contact->getAccountName()); Models::Contact::Messages deque; contact->getMessages(deque); @@ -59,7 +60,6 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): addMessage(*itr); } - line->setMyName(p_contact->getAccountName()); connect(line, SIGNAL(resize(int)), this, SLOT(onMessagesResize(int))); QScrollBar* vs = m_ui->scrollArea->verticalScrollBar(); diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 0397a0c..b7dcb3a 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -152,8 +152,8 @@ void Models::Contact::removePresence(const QString& name) qDebug() << "an attempt to remove non existing presence " << name << " from the contact " << jid << " of account " << getAccountName() << ", skipping"; } else { Presence* pr = itr.value(); - removeChild(pr->row()); presences.erase(itr); + removeChild(pr->row()); pr->deleteLater(); } } diff --git a/ui/models/contact.h b/ui/models/contact.h index 49d4b35..1e3c2fa 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -16,7 +16,7 @@ class Contact : public Item Q_OBJECT public: typedef std::deque Messages; - Contact(const QString& p_jid ,const QMap &data, Item *parentItem = 0); + Contact(const QString& p_jid, const QMap &data, Item *parentItem = 0); ~Contact(); QString getJid() const; diff --git a/ui/models/item.cpp b/ui/models/item.cpp index f5a9465..2f3513b 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -3,10 +3,15 @@ Models::Item::Item(Type p_type, const QMap &p_data, Item *p_parent): QObject(), type(p_type), - name(p_data.value("name").toString()), + name(""), childItems(), parent(p_parent) -{} +{ + QMap::const_iterator itr = p_data.find("name"); + if (itr != p_data.end()) { + setName(itr.value().toString()); + } +} Models::Item::~Item() { diff --git a/ui/models/item.h b/ui/models/item.h index 629561e..67e6598 100644 --- a/ui/models/item.h +++ b/ui/models/item.h @@ -16,7 +16,7 @@ class Item : public QObject{ account, group, contact, - conversation, + room, presence, root }; diff --git a/ui/models/room.cpp b/ui/models/room.cpp new file mode 100644 index 0000000..2a435e3 --- /dev/null +++ b/ui/models/room.cpp @@ -0,0 +1,186 @@ +/* + * + * Copyright (C) 2019 Юрий Губич + * + * 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 "room.h" +#include + +Models::Room::Room(const QString& p_jid, const QMap& data, Models::Item* parentItem): + Item(room, data, parentItem), + autoJoin(false), + joined(false), + jid(p_jid), + nick(""), + messages() +{ + QMap::const_iterator itr = data.find("autoJoin"); + if (itr != data.end()) { + setAutoJoin(itr.value().toBool()); + } + + itr = data.find("joined"); + if (itr != data.end()) { + setJoined(itr.value().toBool()); + } + + itr = data.find("nick"); + if (itr != data.end()) { + setNick(itr.value().toString()); + } +} + +Models::Room::~Room() +{ +} + +unsigned int Models::Room::getUnreadMessagesCount() const +{ + return messages.size(); +} + +int Models::Room::columnCount() const +{ + return 5; +} + +QString Models::Room::getJid() const +{ + return jid; +} + +bool Models::Room::getAutoJoin() const +{ + return autoJoin; +} + +bool Models::Room::getJoined() const +{ + return joined; +} + +QString Models::Room::getNick() const +{ + return nick; +} + +QString Models::Room::getRoomName() const +{ + if (name.size() == 0) { + return jid; + } else { + return name; + } +} + +QVariant Models::Room::data(int column) const +{ + switch (column) { + case 0: + return getRoomName(); + case 1: + return jid; + case 2: + return getJoined(); + case 3: + return getAutoJoin(); + case 4: + return getNick(); + default: + return QVariant(); + } +} + +void Models::Room::setAutoJoin(bool p_autoJoin) +{ + if (autoJoin != p_autoJoin) { + autoJoin = p_autoJoin; + changed(3); + } +} + +void Models::Room::setJid(const QString& p_jid) +{ + if (jid != p_jid) { + jid = p_jid; + changed(1); + } +} + +void Models::Room::setJoined(bool p_joined) +{ + if (joined != p_joined) { + joined = p_joined; + changed(2); + } +} + +void Models::Room::setNick(const QString& p_nick) +{ + if (nick != p_nick) { + nick = p_nick; + changed(4); + } +} + +void Models::Room::update(const QString& field, const QVariant& value) +{ + if (field == "name") { + setName(value.toString()); + } else if (field == "jid") { + setJid(value.toString()); + } else if (field == "joined") { + setJoined(value.toBool()); + } else if (field == "autoJoin") { + setAutoJoin(value.toBool()); + } else if (field == "nick") { + setNick(value.toString()); + } +} + +QIcon Models::Room::getStatusIcon(bool big) const +{ + if (autoJoin) { + if (joined) { + return Shared::connectionStateIcon(Shared::connected, big); + } else { + return Shared::connectionStateIcon(Shared::disconnected, big); + } + } else { + if (joined) { + return Shared::connectionStateIcon(Shared::connecting, big); + } else { + return Shared::connectionStateIcon(Shared::error, big); + } + } +} + +QString Models::Room::getStatusText() const +{ + if (autoJoin) { + if (joined) { + return "Subscribed"; + } else { + return "Temporarily unsubscribed"; + } + } else { + if (joined) { + return "Temporarily subscribed"; + } else { + return "Unsubscribed"; + } + } +} diff --git a/ui/models/room.h b/ui/models/room.h new file mode 100644 index 0000000..640de26 --- /dev/null +++ b/ui/models/room.h @@ -0,0 +1,70 @@ +/* + * + * Copyright (C) 2019 Юрий Губич + * + * 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 MODELS_ROOM_H +#define MODELS_ROOM_H + +#include "item.h" +#include "../global.h" + +namespace Models { + +/** + * @todo write docs + */ +class Room : public Models::Item +{ + Q_OBJECT +public: + Room(const QString& p_jid, const QMap &data, Item *parentItem = 0); + ~Room(); + + int columnCount() const override; + QVariant data(int column) const override; + + unsigned int getUnreadMessagesCount() const; + bool getJoined() const; + bool getAutoJoin() const; + QString getJid() const; + QString getNick() const; + QString getRoomName() const; + + QIcon getStatusIcon(bool big = false) const; + QString getStatusText() const; + + void setJoined(bool p_joined); + void setAutoJoin(bool p_autoJoin); + void setJid(const QString& p_jid); + void setNick(const QString& p_nick); + + void update(const QString& field, const QVariant& value); + +protected: + +private: + bool autoJoin; + bool joined; + QString jid; + QString nick; + std::deque messages; + +}; + +} + +#endif // MODELS_ROOM_H diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index c997219..7ce8690 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -74,21 +74,26 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const break; case Qt::DecorationRole: switch (item->type) { - case Item::account:{ + case Item::account: { Account* acc = static_cast(item); result = acc->getStatusIcon(false); } break; - case Item::contact:{ + case Item::contact: { Contact* contact = static_cast(item); result = contact->getStatusIcon(false); } break; - case Item::presence:{ + case Item::presence: { Presence* presence = static_cast(item); result = presence->getStatusIcon(false); } break; + case Item::room: { + Room* room = static_cast(item); + result = room->getStatusIcon(false); + } + break; default: break; } @@ -172,6 +177,17 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const result = str; } break; + case Item::room: { + Room* rm = static_cast(item); + unsigned int count = rm->getUnreadMessagesCount(); + QString str(""); + if (count > 0) { + str += QString("New messages: ") + std::to_string(count).c_str() + "\n"; + } + str += QString("Subscription: ") + rm->getStatusText() + "\n"; + result = str; + } + break; default: result = ""; break; @@ -313,8 +329,8 @@ void Models::Roster::addGroup(const QString& account, const QString& name) if (itr != accounts.end()) { Account* acc = itr->second; Group* group = new Group({{"name", name}}); - acc->appendChild(group); groups.insert(std::make_pair(id, group)); + acc->appendChild(group); } else { qDebug() << "An attempt to add group " << name << " to non existing account " << account << ", skipping"; } @@ -382,8 +398,8 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons } contact = new Contact(jid, data); - parent->appendChild(contact); contacts.insert(std::make_pair(id, contact)); + parent->appendChild(contact); } void Models::Roster::removeGroup(const QString& account, const QString& name) @@ -638,6 +654,17 @@ void Models::Roster::removeAccount(const QString& account) } } + std::map::const_iterator rItr = rooms.begin(); + while (rItr != rooms.end()) { + if (rItr->first.account == account) { + std::map::const_iterator lItr = rItr; + ++rItr; + rooms.erase(lItr); + } else { + ++rItr; + } + } + acc->deleteLater(); } @@ -650,3 +677,66 @@ QString Models::Roster::getContactName(const QString& account, const QString& ji } return cItr->second->getContactName(); } + +void Models::Roster::addRoom(const QString& account, const QString jid, const QMap& data) +{ + Account* acc; + { + std::map::iterator itr = accounts.find(account); + if (itr == accounts.end()) { + qDebug() << "An attempt to add a room " << jid << " to non existing account " << account << ", skipping"; + return; + } + acc = itr->second; + } + + ElId id = {account, jid}; + std::map::const_iterator itr = rooms.find(id); + if (itr != rooms.end()) { + qDebug() << "An attempt to add already existing room" << jid << ", skipping"; + return; + } + + Room* room = new Room(jid, data); + rooms.insert(std::make_pair(id, room)); + acc->appendChild(room); +} + +void Models::Roster::changeRoom(const QString& account, const QString jid, const QMap& data) +{ + ElId id = {account, jid}; + std::map::const_iterator itr = rooms.find(id); + if (itr == rooms.end()) { + qDebug() << "An attempt to change non existing room" << jid << ", skipping"; + return; + } + Room* room = itr->second; + for (QMap::const_iterator dItr = data.begin(), dEnd = data.end(); dItr != dEnd; ++dItr) { + room->update(dItr.key(), dItr.value()); + } +} + +void Models::Roster::removeRoom(const QString& account, const QString jid) +{ + Account* acc; + { + std::map::iterator itr = accounts.find(account); + if (itr == accounts.end()) { + qDebug() << "An attempt to remove a room " << jid << " from non existing account " << account << ", skipping"; + return; + } + acc = itr->second; + } + + ElId id = {account, jid}; + std::map::const_iterator itr = rooms.find(id); + if (itr == rooms.end()) { + qDebug() << "An attempt to remove non existing room" << jid << ", skipping"; + return; + } + + Room* room = itr->second; + acc->removeChild(room->row()); + room->deleteLater(); + rooms.erase(itr); +} diff --git a/ui/models/roster.h b/ui/models/roster.h index 9471506..4e394b4 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -11,6 +11,7 @@ #include "account.h" #include "contact.h" #include "group.h" +#include "room.h" namespace Models { @@ -36,6 +37,9 @@ public: void removePresence(const QString& account, const QString& jid, const QString& name); void addMessage(const QString& account, const Shared::Message& data); void dropMessages(const QString& account, const QString& jid); + void addRoom(const QString& account, const QString jid, const QMap& data); + void changeRoom(const QString& account, const QString jid, const QMap& data); + void removeRoom(const QString& account, const QString jid); QString getContactName(const QString& account, const QString& jid); QVariant data ( const QModelIndex& index, int role ) const override; @@ -53,6 +57,7 @@ private: std::map accounts; std::map groups; std::multimap contacts; + std::map rooms; private slots: void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 92114a1..f8b7960 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -418,3 +418,18 @@ void Squawk::onRosterContextMenu(const QPoint& point) } } } + +void Squawk::addRoom(const QString& account, const QString jid, const QMap& data) +{ + rosterModel.addRoom(account, jid, data); +} + +void Squawk::changeRoom(const QString& account, const QString jid, const QMap& data) +{ + rosterModel.changeRoom(account, jid, data); +} + +void Squawk::removeRoom(const QString& account, const QString jid) +{ + rosterModel.removeRoom(account, jid); +} diff --git a/ui/squawk.h b/ui/squawk.h index 81f3999..294cf98 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -57,6 +57,9 @@ public slots: void stateChanged(int state); void accountMessage(const QString& account, const Shared::Message& data); void responseArchive(const QString& account, const QString& jid, const std::list& list); + void addRoom(const QString& account, const QString jid, const QMap& data); + void changeRoom(const QString& account, const QString jid, const QMap& data); + void removeRoom(const QString& account, const QString jid); private: typedef std::map Conversations;