From 4775c7b7006b1515d69f599b8a84bdc93722dd57 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 9 Apr 2019 18:04:08 +0300 Subject: [PATCH] basic conversation window binding --- core/account.cpp | 20 +++++ core/account.h | 1 + ui/CMakeLists.txt | 1 + ui/conversation.cpp | 65 +++++++++++++++ ui/conversation.h | 22 ++++- ui/conversation.ui | 181 ++++++++++++++++++++++++++++------------- ui/models/accounts.cpp | 2 +- ui/models/accounts.h | 2 +- ui/models/contact.cpp | 29 +++++-- ui/models/contact.h | 6 +- ui/models/item.cpp | 9 +- ui/models/item.h | 3 +- ui/models/roster.cpp | 2 +- ui/models/roster.h | 6 +- ui/squawk.cpp | 59 +++++++++++++- ui/squawk.h | 5 ++ 16 files changed, 333 insertions(+), 80 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 86bbc6f..f6616a3 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -1,5 +1,6 @@ #include "account.h" #include +#include #include using namespace Core; @@ -20,6 +21,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& 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&))); QXmppRosterManager& rm = client.rosterManager(); @@ -330,3 +332,21 @@ void Core::Account::setResource(const QString& p_resource) { config.setResource(p_resource); } + +void Core::Account::onMessageReceived(const QXmppMessage& message) +{ + qDebug() << "Message received: "; + qDebug() << "- from: " << message.from(); + qDebug() << "- to: " << message.to(); + qDebug() << "- body: " << message.body(); + qDebug() << "- type: " << message.type(); + qDebug() << "- state: " << message.state(); + qDebug() << "- stamp: " << message.stamp(); + qDebug() << "- id: " << message.id(); + qDebug() << "- isAttentionRequested: " << message.isAttentionRequested(); + qDebug() << "- isReceiptRequested: " << message.isReceiptRequested(); + qDebug() << "- receiptId: " << message.receiptId(); + qDebug() << "- subject: " << message.subject(); + qDebug() << "- thread: " << message.thread(); + qDebug() << "- isMarkable: " << message.isMarkable(); +} diff --git a/core/account.h b/core/account.h index 9f3f10e..6dcb8e9 100644 --- a/core/account.h +++ b/core/account.h @@ -65,6 +65,7 @@ private slots: void onRosterItemRemoved(const QString& bareJid); void onRosterPresenceChanged(const QString& bareJid, const QString& resource); void onPresenceReceived(const QXmppPresence& presence); + void onMessageReceived(const QXmppMessage& message); private: void addedAccount(const QString &bareJid); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 3a226cf..a390327 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -19,6 +19,7 @@ set(squawkUI_SRC models/account.cpp models/contact.cpp models/presence.cpp + conversation.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/conversation.cpp b/ui/conversation.cpp index fd75e19..8d9e1d8 100644 --- a/ui/conversation.cpp +++ b/ui/conversation.cpp @@ -18,3 +18,68 @@ #include "conversation.h" #include "ui_conversation.h" +#include + +Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): + QWidget(parent), + contact(p_contact), + m_ui(new Ui::Conversation) +{ + m_ui->setupUi(this); + m_ui->splitter->setSizes({300, 0}); + m_ui->splitter->setStretchFactor(1, 0); + + setName(p_contact->getName()); + setState(p_contact->getAvailability()); + + connect(contact, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onContactChanged(Models::Item*, int, int))); +} + +Conversation::~Conversation() +{ + disconnect(contact, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onContactChanged(Models::Item*, int, int))); +} + +void Conversation::setName(const QString& name) +{ + if (name == "") { + m_ui->nameLabel->setText(getJid()); + } else { + m_ui->nameLabel->setText(name); + } +} + +void Conversation::setState(Shared::Availability state) +{ + m_ui->statusIcon->setPixmap(QIcon::fromTheme(Shared::availabilityThemeIcons[state]).pixmap(50)); + m_ui->statusIcon->setToolTip(Shared::availabilityNames[state]); +} + +void Conversation::setStatus(const QString& status) +{ + m_ui->statusLabel->setText(status); +} + +QString Conversation::getAccount() const +{ + return contact->getAccountName(); +} + +QString Conversation::getJid() const +{ + return contact->getJid(); +} + +void Conversation::onContactChanged(Models::Item* item, int row, int col) +{ + if (item == contact) { + switch (col) { + case 0: + setName(contact->getName()); + break; + case 3: + setState(contact->getAvailability()); + break; + } + } +} diff --git a/ui/conversation.h b/ui/conversation.h index 27fdb38..9d429ad 100644 --- a/ui/conversation.h +++ b/ui/conversation.h @@ -21,20 +21,34 @@ #include #include +#include "../global.h" +#include "models/contact.h" namespace Ui { class Conversation; } -/** - * @todo write docs - */ class Conversation : public QWidget { Q_OBJECT - +public: + Conversation(Models::Contact* p_contact, QWidget* parent = 0); + ~Conversation(); + + QString getJid() const; + QString getAccount() const; + +protected: + void setState(Shared::Availability state); + void setStatus(const QString& status); + void setName(const QString& name); + +protected slots: + void onContactChanged(Models::Item* item, int row, int col); + private: + Models::Contact* contact; QScopedPointer m_ui; }; diff --git a/ui/conversation.ui b/ui/conversation.ui index b549bf3..62087d7 100644 --- a/ui/conversation.ui +++ b/ui/conversation.ui @@ -6,8 +6,8 @@ 0 0 - 478 - 428 + 572 + 377 @@ -54,40 +54,72 @@ + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + - Status icon + - + - User name of JID + - + - Status + - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + - Avatar + - + + + QFrame::NoFrame + + @@ -99,46 +131,29 @@ - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 0 - 30 - - - - - - - - - 0 - 0 - - - - - - - - - - + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + 0 + 0 + + 0 @@ -153,12 +168,16 @@ 0 - + - + + .. + + + true @@ -176,31 +195,81 @@ - + - + + .. + + + true - + - + + .. - false + true + + + + + + + + 0 + 0 + + + + + + + + .. + + + true + + + + + 0 + 0 + + + + + 0 + 30 + + + + + 0 + 30 + + + + QFrame::NoFrame + + + diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index 4a3cf51..f617c84 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -63,7 +63,7 @@ void Models::Accounts::addAccount(Account* account) { beginInsertRows(QModelIndex(), accs.size(), accs.size()); accs.push_back(account); - connect(account, SIGNAL(childChanged(Item*, int, int)), this, SLOT(onAccountChanged(Item*, int, int))); + connect(account, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(onAccountChanged(Models::Item*, int, int))); endInsertRows(); } diff --git a/ui/models/accounts.h b/ui/models/accounts.h index 458795c..ba4f66d 100644 --- a/ui/models/accounts.h +++ b/ui/models/accounts.h @@ -29,7 +29,7 @@ private: static std::deque columns; private slots: - void onAccountChanged(Item* item, int row, int col); + void onAccountChanged(Models::Item* item, int row, int col); }; } diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index fb27780..763fab3 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -81,9 +81,9 @@ QVariant Models::Contact::data(int column) const case 1: return jid; case 2: - return availability; - case 3: return state; + case 3: + return availability; default: return QVariant(); } @@ -124,6 +124,7 @@ void Models::Contact::removePresence(const QString& name) QMap::iterator itr = presences.find(name); if (itr == presences.end()) { + qDebug() << "an attempt to remove non existing presence " << name << " from the contact " << jid << " of account " << getAccountName() << ", skipping"; } else { Presence* pr = itr.value(); presences.erase(itr); @@ -154,6 +155,8 @@ 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())); Item::_removeChild(index); refresh(); } @@ -161,12 +164,7 @@ void Models::Contact::_removeChild(int index) void Models::Contact::appendChild(Models::Item* child) { Item::appendChild(child); - refresh(); -} - -void Models::Contact::changed(int col) -{ - Item::changed(col); + connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); refresh(); } @@ -191,3 +189,18 @@ QIcon Models::Contact::getStatusIcon() const return QIcon::fromTheme(Shared::subscriptionStateThemeIcons[state]); } } + +QString Models::Contact::getAccountName() const +{ + const Item* p = this; + do { + p = p->parentItemConst(); + } while (p != 0 && p->type != Item::account); + + if (p == 0) { + qDebug() << "An attempt to request account name of the contact " << jid << " but the parent account wasn't found, returning empty string"; + return ""; + } + return p->getName(); +} + diff --git a/ui/models/contact.h b/ui/models/contact.h index 4ce42b1..286f0b9 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -30,12 +30,14 @@ public: void removePresence(const QString& name); void appendChild(Models::Item * child) override; + QString getAccountName() const; protected: - void refresh(); - void changed(int col) override; void _removeChild(int index) override; +protected slots: + void refresh(); + protected: void setAvailability(Shared::Availability p_state); void setAvailability(unsigned int p_state); diff --git a/ui/models/item.cpp b/ui/models/item.cpp index 11b038c..50db0bd 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -41,7 +41,7 @@ void Models::Item::appendChild(Models::Item* child) childItems.push_back(child); child->parent = this; - QObject::connect(child, SIGNAL(childChanged(Item*, int, int)), this, SIGNAL(childChanged(Item*, int, int))); + 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))); @@ -87,6 +87,11 @@ Models::Item * Models::Item::parentItem() return parent; } +const Models::Item * Models::Item::parentItemConst() const +{ + return parent; +} + int Models::Item::columnCount() const { return 1; @@ -116,7 +121,7 @@ void Models::Item::_removeChild(int index) { Item* child = childItems[index]; - QObject::connect(child, SIGNAL(childChanged(Item*, int, int)), this, SIGNAL(childChanged(Item*, int, int))); + 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))); diff --git a/ui/models/item.h b/ui/models/item.h index 06944c9..6c71b6c 100644 --- a/ui/models/item.h +++ b/ui/models/item.h @@ -25,7 +25,7 @@ class Item : public QObject{ ~Item(); signals: - void childChanged(Item* item, int row, int col); + void childChanged(Models::Item* item, int row, int col); void childIsAboutToBeInserted(Item* parent, int first, int last); void childInserted(); void childIsAboutToBeRemoved(Item* parent, int first, int last); @@ -45,6 +45,7 @@ class Item : public QObject{ virtual QVariant data(int column) const; int row() const; Item *parentItem(); + const Item *parentItemConst() const; const Type type; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index e18de87..c808fed 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -17,7 +17,7 @@ Models::Roster::Roster(QObject* parent): SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)), this, SLOT(onAccountDataChanged(const QModelIndex&, const QModelIndex&, const QVector&))); - connect(root, SIGNAL(childChanged(Item*, int, int)), this, SLOT(onChildChanged(Item*, int, int))); + 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))); diff --git a/ui/models/roster.h b/ui/models/roster.h index 669afcf..da142f8 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -16,9 +16,9 @@ namespace Models class Roster : public QAbstractItemModel { - class ElId; Q_OBJECT public: + class ElId; Roster(QObject* parent = 0); ~Roster(); @@ -51,7 +51,7 @@ private: private slots: void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles); - void onChildChanged(Item* item, int row, int col); + void onChildChanged(Models::Item* item, int row, int col); void onChildIsAboutToBeInserted(Item* parent, int first, int last); void onChildInserted(); void onChildIsAboutToBeRemoved(Item* parent, int first, int last); @@ -59,7 +59,7 @@ private slots: void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex); void onChildMoved(); -private: +public: class ElId { public: ElId (const QString& p_account, const QString& p_name); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 5164b2a..b23d381 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -7,7 +7,8 @@ Squawk::Squawk(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::Squawk), accounts(0), - rosterModel() + rosterModel(), + conversations() { m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); @@ -19,6 +20,7 @@ Squawk::Squawk(QWidget *parent) : connect(m_ui->actionAccounts, SIGNAL(triggered()), this, SLOT(onAccounts())); connect(m_ui->comboBox, SIGNAL(activated(int)), this, SLOT(onComboboxActivated(int))); + connect(m_ui->roster, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onRosterItemDoubleClicked(const QModelIndex&))); //m_ui->mainToolBar->addWidget(m_ui->comboBox); } @@ -47,6 +49,11 @@ 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*))); + itr->second->close(); + } + conversations.clear(); QMainWindow::closeEvent(event); } @@ -144,3 +151,53 @@ void Squawk::stateChanged(int state) m_ui->comboBox->setCurrentIndex(state); } +void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) +{ + if (item.isValid()) { + Models::Item* node = static_cast(item.internalPointer()); + Models::Contact* contact = 0; + switch (node->type) { + case Models::Item::contact: + contact = static_cast(node); + break; + case Models::Item::presence: + contact = static_cast(node->parentItem()); + break; + default: + m_ui->roster->expand(item); + break; + } + + if (contact != 0) { + QString jid = contact->getJid(); + QString account = contact->getAccountName(); + Models::Roster::ElId id(account, jid); + Conversations::const_iterator itr = conversations.find(id); + if (itr != conversations.end()) { + itr->second->show(); + itr->second->raise(); + itr->second->activateWindow(); + } else { + Conversation* conv = new Conversation(contact); + + conv->setAttribute(Qt::WA_DeleteOnClose); + connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); + + conversations.insert(std::make_pair(id, conv)); + + conv->show(); + } + } + } +} + +void Squawk::onConversationClosed(QObject* parent) +{ + Conversation* conv = static_cast(sender()); + Conversations::const_iterator itr = conversations.find({conv->getAccount(), conv->getJid()}); + if (itr == conversations.end()) { + qDebug() << "Conversation has been closed but can not be found among other opened conversations, application is most probably going to crash"; + return; + } + conversations.erase(itr); +} diff --git a/ui/squawk.h b/ui/squawk.h index a80fb78..08bd5e0 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -8,6 +8,7 @@ #include #include "accounts.h" +#include "conversation.h" #include "models/roster.h" #include "../global.h" @@ -45,10 +46,12 @@ public slots: void stateChanged(int state); private: + typedef std::map Conversations; QScopedPointer m_ui; Accounts* accounts; Models::Roster rosterModel; + Conversations conversations; protected: void closeEvent(QCloseEvent * event) override; @@ -56,7 +59,9 @@ protected: private slots: void onAccounts(); void onAccountsClosed(QObject* parent = 0); + void onConversationClosed(QObject* parent = 0); void onComboboxActivated(int index); + void onRosterItemDoubleClicked(const QModelIndex& item); };