diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 52913a8..0ed806f 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -9,6 +9,10 @@ set(CMAKE_AUTOUIC ON) # Find the QtWidgets library find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5DBus CONFIG REQUIRED) +find_package(Boost 1.36.0 CONFIG REQUIRED) +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) +endif() add_subdirectory(widgets) @@ -25,6 +29,8 @@ set(squawkUI_SRC models/abstractparticipant.cpp models/participant.cpp models/reference.cpp + models/messagefeed.cpp + models/element.cpp utils/messageline.cpp utils//message.cpp utils/resizer.cpp diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 57744d8..d54fccf 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -17,55 +17,26 @@ */ #include "contact.h" -#include "account.h" #include Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap &data, Item *parentItem): - Item(Item::contact, data, parentItem), - jid(p_jid), + Element(Item::contact, acc, p_jid, data, parentItem), availability(Shared::Availability::offline), state(Shared::SubscriptionState::none), - avatarState(Shared::Avatar::empty), presences(), - messages(), - childMessages(0), - status(), - avatarPath(), - account(acc) + status() { 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() { } -QString Models::Contact::getJid() const -{ - return jid; -} - -void Models::Contact::setJid(const QString p_jid) -{ - if (jid != p_jid) { - jid = p_jid; - changed(1); - } -} - void Models::Contact::setAvailability(unsigned int p_state) { setAvailability(Shared::Global::fromInt(p_state)); @@ -144,16 +115,12 @@ void Models::Contact::update(const QString& field, const QVariant& value) { if (field == "name") { setName(value.toString()); - } else if (field == "jid") { - setJid(value.toString()); } else if (field == "availability") { setAvailability(value.toUInt()); } else if (field == "state") { setState(value.toUInt()); - } else if (field == "avatarState") { - setAvatarState(value.toUInt()); - } else if (field == "avatarPath") { - setAvatarPath(value.toString()); + } else { + Element::update(field, value); } } @@ -192,11 +159,9 @@ void Models::Contact::refresh() { QDateTime lastActivity; Presence* presence = 0; - unsigned int count = 0; for (QMap::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { Presence* pr = itr.value(); QDateTime la = pr->getLastActivity(); - count += pr->getMessagesCount(); if (la > lastActivity) { lastActivity = la; @@ -211,11 +176,6 @@ void Models::Contact::refresh() setAvailability(Shared::Availability::offline); setStatus(""); } - - if (childMessages != count) { - childMessages = count; - changed(4); - } } void Models::Contact::_removeChild(int index) @@ -257,81 +217,6 @@ QIcon Models::Contact::getStatusIcon(bool big) const } } -void Models::Contact::addMessage(const Shared::Message& data) -{ - const QString& res = data.getPenPalResource(); - if (res.size() > 0) { - QMap::iterator itr = presences.find(res); - if (itr == presences.end()) { - // this is actually the place when I can spot someone's invisible presence, and there is nothing criminal in it, cuz the sender sent us a message - // therefore he have revealed himself - // the only issue is to find out when the sender is gone offline - Presence* pr = new Presence({}); - pr->setName(res); - pr->setAvailability(Shared::Availability::invisible); - pr->setLastActivity(QDateTime::currentDateTimeUtc()); - presences.insert(res, pr); - appendChild(pr); - pr->addMessage(data); - return; - } - itr.value()->addMessage(data); - } else { - messages.emplace_back(data); - changed(4); - } -} - -void Models::Contact::changeMessage(const QString& id, const QMap& data) -{ - - bool found = false; - for (Shared::Message& msg : messages) { - if (msg.getId() == id) { - msg.change(data); - found = true; - break; - } - } - if (!found) { - for (Presence* pr : presences) { - found = pr->changeMessage(id, data); - if (found) { - break; - } - } - } -} - -unsigned int Models::Contact::getMessagesCount() const -{ - return messages.size() + childMessages; -} - -void Models::Contact::dropMessages() -{ - if (messages.size() > 0) { - messages.clear(); - changed(4); - } - - for (QMap::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { - itr.value()->dropMessages(); - } -} - -void Models::Contact::getMessages(Models::Contact::Messages& container) const -{ - for (Messages::const_iterator itr = messages.begin(), end = messages.end(); itr != end; ++itr) { - const Shared::Message& msg = *itr; - container.push_back(msg); - } - - for (QMap::const_iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { - itr.value()->getMessages(container); - } -} - void Models::Contact::toOfflineState() { std::deque::size_type size = childItems.size(); @@ -355,75 +240,3 @@ QString Models::Contact::getDisplayedName() const return getContactName(); } -bool Models::Contact::columnInvolvedInDisplay(int col) -{ - return Item::columnInvolvedInDisplay(col) && col == 1; -} - -Models::Contact * Models::Contact::copy() const -{ - Contact* cnt = new Contact(*this); - return cnt; -} - -Models::Contact::Contact(const Models::Contact& other): - Item(other), - jid(other.jid), - availability(other.availability), - state(other.state), - presences(), - messages(other.messages), - childMessages(0), - account(other.account) -{ - for (const Presence* pres : other.presences) { - Presence* pCopy = new Presence(*pres); - presences.insert(pCopy->getName(), pCopy); - Item::appendChild(pCopy); - connect(pCopy, &Item::childChanged, this, &Contact::refresh); - } - - refresh(); -} - -QString Models::Contact::getAvatarPath() const -{ - return avatarPath; -} - -Shared::Avatar Models::Contact::getAvatarState() const -{ - return avatarState; -} - -void Models::Contact::setAvatarPath(const QString& path) -{ - if (path != avatarPath) { - avatarPath = path; - changed(7); - } -} - -void Models::Contact::setAvatarState(Shared::Avatar p_state) -{ - if (avatarState != p_state) { - avatarState = p_state; - changed(6); - } -} - -void Models::Contact::setAvatarState(unsigned int p_state) -{ - if (p_state <= static_cast(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"; - } -} - -const Models::Account * Models::Contact::getParentAccount() const -{ - return account; -} - diff --git a/ui/models/contact.h b/ui/models/contact.h index c8c99b5..7e76f5b 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -19,7 +19,7 @@ #ifndef MODELS_CONTACT_H #define MODELS_CONTACT_H -#include "item.h" +#include "element.h" #include "presence.h" #include "shared/enums.h" #include "shared/message.h" @@ -31,49 +31,34 @@ #include namespace Models { -class Account; -class Contact : public Item +class Contact : public Element { Q_OBJECT public: - typedef std::deque Messages; Contact(const Account* acc, const QString& p_jid, const QMap &data, Item *parentItem = 0); - Contact(const Contact& other); ~Contact(); - 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; QVariant data(int column) const override; - void update(const QString& field, const QVariant& value); + void update(const QString& field, const QVariant& value) override; void addPresence(const QString& name, const QMap& data); void removePresence(const QString& name); QString getContactName() const; QString getStatus() const; - - void addMessage(const Shared::Message& data); - void changeMessage(const QString& id, const QMap& data); - unsigned int getMessagesCount() const; - void dropMessages(); - void getMessages(Messages& container) const; QString getDisplayedName() const override; - Contact* copy() const; - protected: void _removeChild(int index) override; void _appendChild(Models::Item * child) override; - bool columnInvolvedInDisplay(int col) override; - const Account* getParentAccount() const override; protected slots: void refresh(); @@ -84,23 +69,13 @@ 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); private: - QString jid; Shared::Availability availability; Shared::SubscriptionState state; - Shared::Avatar avatarState; QMap presences; - Messages messages; - unsigned int childMessages; QString status; - QString avatarPath; - const Account* account; }; } diff --git a/ui/models/element.cpp b/ui/models/element.cpp new file mode 100644 index 0000000..98a54a5 --- /dev/null +++ b/ui/models/element.cpp @@ -0,0 +1,151 @@ +/* + * 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 "element.h" +#include "account.h" + +#include + +Models::Element::Element(Type p_type, const Models::Account* acc, const QString& p_jid, const QMap& data, Models::Item* parentItem): + Item(p_type, data, parentItem), + jid(p_jid), + avatarPath(), + avatarState(Shared::Avatar::empty), + account(acc), + feed(new MessageFeed()) +{ + connect(feed, &MessageFeed::requestArchive, this, &Element::requestArchive); + + QMap::const_iterator itr = data.find("avatarState"); + if (itr != data.end()) { + setAvatarState(itr.value().toUInt()); + } + itr = data.find("avatarPath"); + if (itr != data.end()) { + setAvatarPath(itr.value().toString()); + } +} + +Models::Element::~Element() +{ + delete feed; +} + + +QString Models::Element::getJid() const +{ + return jid; +} + +void Models::Element::setJid(const QString& p_jid) +{ + if (jid != p_jid) { + jid = p_jid; + changed(1); + } +} + +void Models::Element::update(const QString& field, const QVariant& value) +{ + if (field == "jid") { + setJid(value.toString()); + } else if (field == "avatarState") { + setAvatarState(value.toUInt()); + } else if (field == "avatarPath") { + setAvatarPath(value.toString()); + } +} + +QString Models::Element::getAvatarPath() const +{ + return avatarPath; +} + +Shared::Avatar Models::Element::getAvatarState() const +{ + return avatarState; +} + +void Models::Element::setAvatarPath(const QString& path) +{ + if (path != avatarPath) { + avatarPath = path; + if (type == contact) { + changed(7); + } else if (type == room) { + changed(8); + } + } +} + +void Models::Element::setAvatarState(Shared::Avatar p_state) +{ + if (avatarState != p_state) { + avatarState = p_state; + if (type == contact) { + changed(6); + } else if (type == room) { + changed(7); + } + } +} + +void Models::Element::setAvatarState(unsigned int p_state) +{ + if (p_state <= static_cast(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 element" << jid << ", skipping"; + } +} + +bool Models::Element::columnInvolvedInDisplay(int col) +{ + return Item::columnInvolvedInDisplay(col) && col == 1; +} + +const Models::Account * Models::Element::getParentAccount() const +{ + return account; +} + +unsigned int Models::Element::getMessagesCount() const +{ + return feed->unreadMessagesCount(); +} + +void Models::Element::addMessage(const Shared::Message& data) +{ + feed->addMessage(data); + if (type == contact) { + changed(4); + } else if (type == room) { + changed(5); + } +} + +void Models::Element::changeMessage(const QString& id, const QMap& data) +{ + +} + +void Models::Element::responseArchive(const std::list list) +{ + feed->responseArchive(list); +} diff --git a/ui/models/element.h b/ui/models/element.h new file mode 100644 index 0000000..29c4e76 --- /dev/null +++ b/ui/models/element.h @@ -0,0 +1,70 @@ +/* + * 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 ELEMENT_H +#define ELEMENT_H + +#include "item.h" +#include "messagefeed.h" + +namespace Models { + +class Element : public Item +{ + Q_OBJECT +protected: + Element(Type p_type, const Account* acc, const QString& p_jid, const QMap &data, Item *parentItem = 0); + ~Element(); + +public: + QString getJid() const; + Shared::Avatar getAvatarState() const; + QString getAvatarPath() const; + + virtual void update(const QString& field, const QVariant& value); + + void addMessage(const Shared::Message& data); + void changeMessage(const QString& id, const QMap& data); + unsigned int getMessagesCount() const; + void responseArchive(const std::list list); + +signals: + void requestArchive(const QString& before); + +protected: + void setJid(const QString& p_jid); + void setAvatarState(Shared::Avatar p_state); + void setAvatarState(unsigned int p_state); + void setAvatarPath(const QString& path); + bool columnInvolvedInDisplay(int col) override; + const Account* getParentAccount() const override; + +protected: + QString jid; + QString avatarPath; + Shared::Avatar avatarState; + + const Account* account; + +public: + MessageFeed* feed; +}; + +} + +#endif // ELEMENT_H diff --git a/ui/models/messagefeed.cpp b/ui/models/messagefeed.cpp new file mode 100644 index 0000000..bd86b67 --- /dev/null +++ b/ui/models/messagefeed.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 "messagefeed.h" + +#include + +MessageFeed::MessageFeed(QObject* parent): + QAbstractListModel(parent), + storage(), + indexById(storage.get()), + indexByTime(storage.get