1
0
forked from blue/squawk

basic conversation window binding

This commit is contained in:
Blue 2019-04-09 18:04:08 +03:00
parent 1cbcad44af
commit 4775c7b700
16 changed files with 333 additions and 80 deletions

View File

@ -1,5 +1,6 @@
#include "account.h"
#include <qxmpp/QXmppRosterManager.h>
#include <qxmpp/QXmppMessage.h>
#include <QDateTime>
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();
}

View File

@ -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);

View File

@ -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

View File

@ -18,3 +18,68 @@
#include "conversation.h"
#include "ui_conversation.h"
#include <QDebug>
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;
}
}
}

View File

@ -21,20 +21,34 @@
#include <QWidget>
#include <QScopedPointer>
#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<Ui::Conversation> m_ui;
};

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>478</width>
<height>428</height>
<width>572</width>
<height>377</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
@ -54,40 +54,72 @@
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="statusIcon">
<property name="text">
<string>Status icon</string>
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>User name of JID</string>
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>Status</string>
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="avatar">
<property name="text">
<string>Avatar</string>
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="textBrowser"/>
<widget class="QTextBrowser" name="dialogBox">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</widget>
</item>
</layout>
</widget>
@ -99,46 +131,29 @@
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0" rowspan="2">
<widget class="QPlainTextEdit" name="plainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="leftMargin">
<number>0</number>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
<property name="topMargin">
<number>0</number>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>30</height>
</size>
<property name="rightMargin">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="1" rowspan="2">
<widget class="QPushButton" name="pushButton_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="text">
<string/>
<property name="spacing">
<number>0</number>
</property>
<property name="icon">
<iconset theme="document-send"/>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QWidget" name="widget_3" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
@ -153,12 +168,16 @@
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pushButton_2">
<widget class="QPushButton" name="smilesButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="smiley-shape"/>
<iconset theme="smiley-shape">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
@ -176,31 +195,81 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<widget class="QPushButton" name="attachButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="document-send-symbolic"/>
<iconset theme="document-send-symbolic">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<widget class="QPushButton" name="clearButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="flat">
<bool>false</bool>
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="document-send">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPlainTextEdit" name="messageEditor">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -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();
}

View File

@ -29,7 +29,7 @@ private:
static std::deque<QString> columns;
private slots:
void onAccountChanged(Item* item, int row, int col);
void onAccountChanged(Models::Item* item, int row, int col);
};
}

View File

@ -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<QString, Presence*>::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();
}

View File

@ -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);

View File

@ -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)));

View File

@ -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;

View File

@ -17,7 +17,7 @@ Models::Roster::Roster(QObject* parent):
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)),
this,
SLOT(onAccountDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
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)));

View File

@ -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<int>& 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);

View File

@ -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<Models::Item*>(item.internalPointer());
Models::Contact* contact = 0;
switch (node->type) {
case Models::Item::contact:
contact = static_cast<Models::Contact*>(node);
break;
case Models::Item::presence:
contact = static_cast<Models::Contact*>(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<Conversation*>(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);
}

View File

@ -8,6 +8,7 @@
#include <map>
#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<Models::Roster::ElId, Conversation*> Conversations;
QScopedPointer<Ui::Squawk> 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);
};