diff --git a/core/conference.cpp b/core/conference.cpp index 3124da5..f205702 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -132,7 +132,6 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) if (resource == jid) { qDebug() << "Room" << jid << "is reporting of adding itself to the list participants. Not sure what to do with that yet, skipping"; } else { - qDebug() << "Participant" << resource << "had entered room" << jid; QXmppPresence pres = room->participantPresence(jid); QDateTime lastInteraction = pres.lastUserInteraction(); if (!lastInteraction.isValid()) { diff --git a/core/squawk.cpp b/core/squawk.cpp index fa8be41..a5d5f94 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -126,6 +126,13 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const 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&))); + QMap map = { {"login", login}, diff --git a/global.h b/global.h index 8295278..cf0151e 100644 --- a/global.h +++ b/global.h @@ -51,12 +51,35 @@ enum SubscriptionState { unknown }; +enum class Affiliation { + unspecified, + outcast, + nobody, + member, + admin, + owner +}; + +enum class Role { + unspecified, + nobody, + visitor, + participant, + moderator +}; + static const Availability availabilityHighest = offline; static const Availability availabilityLowest = online; static const SubscriptionState subscriptionStateHighest = unknown; static const SubscriptionState subscriptionStateLowest = none; +static const Affiliation affiliationHighest = Affiliation::owner; +static const Affiliation affiliationLowest = Affiliation::unspecified; + +static const Role roleHighest = Role::moderator; +static const Role roleLowest = Role::unspecified; + static const std::deque connectionStateNames = {"Disconnected", "Connecting", "Connected", "Error"}; static const std::deque connectionStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"}; diff --git a/main.cpp b/main.cpp index 4c48fa0..1183137 100644 --- a/main.cpp +++ b/main.cpp @@ -80,7 +80,6 @@ int main(int argc, char *argv[]) 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))); @@ -109,6 +108,12 @@ int main(int argc, char *argv[]) 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&))); //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index e56d8fd..65bdec5 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -23,6 +23,7 @@ set(squawkUI_SRC models/group.cpp models/room.cpp models/abstractparticipant.cpp + models/participant.cpp widgets/conversation.cpp widgets/chat.cpp widgets/room.cpp diff --git a/ui/models/abstractparticipant.cpp b/ui/models/abstractparticipant.cpp index 6563807..7fef30a 100644 --- a/ui/models/abstractparticipant.cpp +++ b/ui/models/abstractparticipant.cpp @@ -38,7 +38,7 @@ Models::AbstractParticipant::~AbstractParticipant() int Models::AbstractParticipant::columnCount() const { - return 5; + return 4; } QVariant Models::AbstractParticipant::data(int column) const diff --git a/ui/models/participant.cpp b/ui/models/participant.cpp new file mode 100644 index 0000000..16f5cb7 --- /dev/null +++ b/ui/models/participant.cpp @@ -0,0 +1,115 @@ +/* + * 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 "participant.h" + +#include + +Models::Participant::Participant(const QMap& data, Models::Item* parentItem): + AbstractParticipant(participant, data, parentItem), + affiliation(Shared::Affiliation::unspecified), + role(Shared::Role::unspecified) +{ + QMap::const_iterator itr = data.find("affiliation"); + if (itr != data.end()) { + setAffiliation(itr.value().toUInt()); + } + + itr = data.find("role"); + if (itr != data.end()) { + setRole(itr.value().toUInt()); + } +} + +Models::Participant::~Participant() +{ +} + +int Models::Participant::columnCount() const +{ + return 6; +} + +QVariant Models::Participant::data(int column) const +{ + switch (column) { + case 4: + return static_cast(affiliation); + case 5: + return static_cast(role); + default: + return AbstractParticipant::data(column); + } +} + +void Models::Participant::update(const QString& key, const QVariant& value) +{ + if (key == "affiliation") { + setAffiliation(value.toUInt()); + } else if (key == "role") { + setRole(value.toUInt()); + } else { + AbstractParticipant::update(key, value); + } +} + +Shared::Affiliation Models::Participant::getAffiliation() const +{ + return affiliation; +} + +void Models::Participant::setAffiliation(Shared::Affiliation p_aff) +{ + if (p_aff != affiliation) { + affiliation = p_aff; + changed(4); + } +} + +void Models::Participant::setAffiliation(unsigned int aff) +{ + if (aff <= static_cast(Shared::affiliationHighest)) { + Shared::Affiliation affil = static_cast(aff); + setAffiliation(affil); + } else { + qDebug() << "An attempt to set wrong affiliation" << aff << "to the room participant" << name; + } +} + +Shared::Role Models::Participant::getRole() const +{ + return role; +} + +void Models::Participant::setRole(Shared::Role p_role) +{ + if (p_role != role) { + role = p_role; + changed(5); + } +} + +void Models::Participant::setRole(unsigned int p_role) +{ + if (p_role <= static_cast(Shared::roleHighest)) { + Shared::Role r = static_cast(p_role); + setRole(r); + } else { + qDebug() << "An attempt to set wrong role" << p_role << "to the room participant" << name; + } +} diff --git a/ui/models/participant.h b/ui/models/participant.h new file mode 100644 index 0000000..9c6ddbc --- /dev/null +++ b/ui/models/participant.h @@ -0,0 +1,52 @@ +/* + * 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 MODELS_PARTICIPANT_H +#define MODELS_PARTICIPANT_H + +#include "abstractparticipant.h" + +namespace Models { + +class Participant : public Models::AbstractParticipant +{ +public: + Participant(const QMap &data, Item *parentItem = 0); + ~Participant(); + + int columnCount() const override; + QVariant data(int column) const override; + + void update(const QString& key, const QVariant& value) override; + + Shared::Affiliation getAffiliation() const; + void setAffiliation(Shared::Affiliation p_aff); + void setAffiliation(unsigned int aff); + + Shared::Role getRole() const; + void setRole(Shared::Role p_role); + void setRole(unsigned int role); + +private: + Shared::Affiliation affiliation; + Shared::Role role; +}; + +} + +#endif // MODELS_PARTICIPANT_H diff --git a/ui/models/presence.cpp b/ui/models/presence.cpp index 20ddb06..b6be0fe 100644 --- a/ui/models/presence.cpp +++ b/ui/models/presence.cpp @@ -28,6 +28,11 @@ Models::Presence::~Presence() { } +int Models::Presence::columnCount() const +{ + return 5; +} + QVariant Models::Presence::data(int column) const { switch (column) { diff --git a/ui/models/presence.h b/ui/models/presence.h index b6b9df9..c396f0c 100644 --- a/ui/models/presence.h +++ b/ui/models/presence.h @@ -33,7 +33,8 @@ public: typedef std::deque Messages; explicit Presence(const QMap &data, Item *parentItem = 0); ~Presence(); - + + int columnCount() const override; QVariant data(int column) const override; QIcon getStatusIcon(bool big = false) const override; diff --git a/ui/models/room.cpp b/ui/models/room.cpp index ab56b11..24e1399 100644 --- a/ui/models/room.cpp +++ b/ui/models/room.cpp @@ -18,6 +18,7 @@ #include "room.h" #include +#include Models::Room::Room(const QString& p_jid, const QMap& data, Models::Item* parentItem): Item(room, data, parentItem), @@ -25,7 +26,8 @@ Models::Room::Room(const QString& p_jid, const QMap& data, Mo joined(false), jid(p_jid), nick(""), - messages() + messages(), + participants() { QMap::const_iterator itr = data.find("autoJoin"); if (itr != data.end()) { @@ -127,6 +129,9 @@ void Models::Room::setJoined(bool p_joined) if (joined != p_joined) { joined = p_joined; changed(2); + if (!joined) { + toOfflineState(); + } } } @@ -217,3 +222,67 @@ void Models::Room::getMessages(Models::Room::Messages& container) const container.push_back(msg); } } + +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(); + } + childItems.clear(); + participants.clear(); + emit childRemoved(); +} + +void Models::Room::addParticipant(const QString& p_name, const QMap& data) +{ + std::map::const_iterator itr = participants.find(p_name); + if (itr != participants.end()) { + qDebug() << "An attempt to add already existing participant" << p_name << "to the room" << name << ", updating instead"; + handleParticipantUpdate(itr, data); + } else { + Participant* part = new Participant(data); + part->setName(p_name); + participants.insert(std::make_pair(p_name, part)); + appendChild(part); + } +} + +void Models::Room::changeParticipant(const QString& p_name, const QMap& data) +{ + std::map::const_iterator itr = participants.find(p_name); + if (itr == participants.end()) { + qDebug() << "An attempt to change non existing participant" << p_name << "from the room" << name << ", skipping"; + } else { + handleParticipantUpdate(itr, data); + } +} + +void Models::Room::removeParticipant(const QString& p_name) +{ + std::map::const_iterator itr = participants.find(p_name); + if (itr == participants.end()) { + qDebug() << "An attempt to remove non existing participant" << p_name << "from the room" << name << ", skipping"; + } else { + Participant* p = itr->second; + participants.erase(itr); + removeChild(p->row()); + p->deleteLater(); + } +} + +void Models::Room::handleParticipantUpdate(std::map::const_iterator itr, const QMap& data) +{ + Participant* part = itr->second; + const QString& p_name = itr->first; + for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { + part->update(itr.key(), itr.value()); + } + if (p_name != part->getName()) { + participants.erase(itr); + participants.insert(std::make_pair(part->getName(), part)); + } +} diff --git a/ui/models/room.h b/ui/models/room.h index 2cf8fe5..bb443f5 100644 --- a/ui/models/room.h +++ b/ui/models/room.h @@ -20,6 +20,7 @@ #define MODELS_ROOM_H #include "item.h" +#include "participant.h" #include "../global.h" namespace Models { @@ -59,8 +60,15 @@ public: unsigned int getMessagesCount() const; void dropMessages(); void getMessages(Messages& container) const; - -protected: + + void addParticipant(const QString& name, const QMap& data); + void changeParticipant(const QString& name, const QMap& data); + void removeParticipant(const QString& name); + + void toOfflineState() override; + +private: + void handleParticipantUpdate(std::map::const_iterator itr, const QMap& data); private: bool autoJoin; @@ -68,6 +76,7 @@ private: QString jid; QString nick; Messages messages; + std::map participants; }; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 7e3ea4e..d0a7dcc 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -783,3 +783,39 @@ void Models::Roster::removeRoom(const QString& account, const QString jid) room->deleteLater(); rooms.erase(itr); } + +void Models::Roster::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + ElId id = {account, jid}; + std::map::const_iterator itr = rooms.find(id); + if (itr == rooms.end()) { + qDebug() << "An attempt to add participant" << name << "non existing room" << jid << "of an account" << account << ", skipping"; + return; + } else { + itr->second->addParticipant(name, data); + } +} + +void Models::Roster::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + ElId id = {account, jid}; + std::map::const_iterator itr = rooms.find(id); + if (itr == rooms.end()) { + qDebug() << "An attempt change participant" << name << "of non existing room" << jid << "of an account" << account << ", skipping"; + return; + } else { + itr->second->changeParticipant(name, data); + } +} + +void Models::Roster::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) +{ + ElId id = {account, jid}; + std::map::const_iterator itr = rooms.find(id); + if (itr == rooms.end()) { + qDebug() << "An attempt remove participant" << name << "from non existing room" << jid << "of an account" << account << ", skipping"; + return; + } else { + itr->second->removeParticipant(name); + } +} diff --git a/ui/models/roster.h b/ui/models/roster.h index ad697e8..314e92d 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -58,6 +58,9 @@ public: 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); + void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); + void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); + void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); QString getContactName(const QString& account, const QString& jid); QVariant data ( const QModelIndex& index, int role ) const override; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 4dccdf2..4117c97 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -480,3 +480,18 @@ void Squawk::removeRoom(const QString& account, const QString jid) { rosterModel.removeRoom(account, jid); } + +void Squawk::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + rosterModel.addRoomParticipant(account, jid, name, data); +} + +void Squawk::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) +{ + rosterModel.changeRoomParticipant(account, jid, name, data); +} + +void Squawk::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) +{ + rosterModel.removeRoomParticipant(account, jid, name); +} diff --git a/ui/squawk.h b/ui/squawk.h index ae26bd3..f1f5803 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -81,6 +81,9 @@ public slots: 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); + void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); + void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); + void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); private: typedef std::map Conversations;