Basic presence with subnodes

This commit is contained in:
Blue 2019-04-07 17:02:41 +03:00
parent de21036456
commit e8eaced6e9
20 changed files with 576 additions and 77 deletions

View File

@ -1,5 +1,6 @@
#include "account.h" #include "account.h"
#include <qxmpp/QXmppRosterManager.h> #include <qxmpp/QXmppRosterManager.h>
#include <QDateTime>
using namespace Core; using namespace Core;
@ -15,7 +16,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(connected()), this, SLOT(onClientConnected()));
QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
QObject::connect(&client, SIGNAL(presenceReceived(const QXmppPresence&)), this, SLOT(onPresenceReceived(const QXmppPresence&)));
QXmppRosterManager& rm = client.rosterManager(); QXmppRosterManager& rm = client.rosterManager();
@ -23,6 +24,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
QObject::connect(&rm, SIGNAL(itemAdded(const QString&)), this, SLOT(onRosterItemAdded(const QString&))); QObject::connect(&rm, SIGNAL(itemAdded(const QString&)), this, SLOT(onRosterItemAdded(const QString&)));
QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&))); QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&)));
QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&))); QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&)));
//QObject::connect(&rm, SIGNAL(presenceChanged(const QString&, const QString&)), this, SLOT(onRosterPresenceChanged(const QString&, const QString&)));
} }
Account::~Account() Account::~Account()
@ -116,6 +118,9 @@ void Core::Account::onRosterItemChanged(const QString& bareJid)
QSet<QString> newGroups = re.groups(); QSet<QString> newGroups = re.groups();
QSet<QString> oldGroups; QSet<QString> oldGroups;
QStringList res = rm.getResources(bareJid);
emit changeContact(bareJid, re.name()); emit changeContact(bareJid, re.name());
for (std::map<QString, std::set<QString>>::iterator itr = groups.begin(), end = groups.end(); itr != end; ++itr) { for (std::map<QString, std::set<QString>>::iterator itr = groups.begin(), end = groups.end(); itr != end; ++itr) {
@ -204,3 +209,50 @@ void Core::Account::addedAccount(const QString& jid)
} }
} }
void Core::Account::onPresenceReceived(const QXmppPresence& presence)
{
QString id = presence.from();
QStringList comps = id.split("/");
QString jid = comps.front();
QString resource = comps.back();
switch (presence.type()) {
case QXmppPresence::Error:
qDebug() << "An error reported by presence from " << id;
break;
case QXmppPresence::Available:{
QDateTime lastInteraction = presence.lastUserInteraction();
if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTime();
}
emit addPresence(jid, resource, {
{"lastActivity", lastInteraction},
{"availability", presence.availableStatusType()}, //TODO check and handle invisible
{"status", presence.statusText()}
});
}
break;
case QXmppPresence::Unavailable:
emit removePresence(jid, resource);
break;
case QXmppPresence::Subscribe:
qDebug("xmpp presence \"subscribe\" received, do not yet know what to do, skipping");
case QXmppPresence::Subscribed:
qDebug("xmpp presence \"subscribed\" received, do not yet know what to do, skipping");
case QXmppPresence::Unsubscribe:
qDebug("xmpp presence \"unsubscribe\" received, do not yet know what to do, skipping");
case QXmppPresence::Unsubscribed:
qDebug("xmpp presence \"unsubscribed\" received, do not yet know what to do, skipping");
case QXmppPresence::Probe:
qDebug("xmpp presence \"probe\" received, do not yet know what to do, skipping");
break;
}
}
void Core::Account::onRosterPresenceChanged(const QString& bareJid, const QString& resource)
{
//not used for now;
qDebug() << "presence changed for " << bareJid << " resource " << resource;
const QXmppPresence& presence = client.rosterManager().getPresence(bareJid, resource);
}

View File

@ -35,6 +35,8 @@ signals:
void removeContact(const QString& jid); void removeContact(const QString& jid);
void removeContact(const QString& jid, const QString& group); void removeContact(const QString& jid, const QString& group);
void changeContact(const QString& jid, const QString& name); void changeContact(const QString& jid, const QString& name);
void addPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& jid, const QString& name);
private: private:
QString name; QString name;
@ -49,9 +51,11 @@ private slots:
void onClientConnected(); void onClientConnected();
void onClientDisconnected(); void onClientDisconnected();
void onRosterReceived(); void onRosterReceived();
void onRosterItemAdded(const QString &bareJid); void onRosterItemAdded(const QString& bareJid);
void onRosterItemChanged(const QString &bareJid); void onRosterItemChanged(const QString& bareJid);
void onRosterItemRemoved(const QString &bareJid); void onRosterItemRemoved(const QString& bareJid);
void onRosterPresenceChanged(const QString& bareJid, const QString& resource);
void onPresenceReceived(const QXmppPresence& presence);
private: private:
void addedAccount(const QString &bareJid); void addedAccount(const QString &bareJid);

View File

@ -80,6 +80,9 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
connect(acc, SIGNAL(removeContact(const QString&)), this, SLOT(onAccountRemoveContact(const QString&))); connect(acc, SIGNAL(removeContact(const QString&)), this, SLOT(onAccountRemoveContact(const QString&)));
connect(acc, SIGNAL(removeContact(const QString&, const QString&)), this, SLOT(onAccountRemoveContact(const QString&, const QString&))); connect(acc, SIGNAL(removeContact(const QString&, const QString&)), this, SLOT(onAccountRemoveContact(const QString&, const QString&)));
connect(acc, SIGNAL(changeContact(const QString&, const QString&)), this, SLOT(onAccountChangeContact(const QString&, const QString&))); connect(acc, SIGNAL(changeContact(const QString&, const QString&)), this, SLOT(onAccountChangeContact(const QString&, const QString&)));
connect(acc, SIGNAL(addPresence(const QString&, const QString&, const QMap<QString, QVariant>&)),
this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&)));
QMap<QString, QVariant> map = { QMap<QString, QVariant> map = {
{"login", login}, {"login", login},
@ -153,3 +156,15 @@ void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& gro
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removeContact(acc->getName(), jid, group); emit removeContact(acc->getName(), jid, group);
} }
void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender());
emit addPresence(acc->getName(), jid, name, data);
}
void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name)
{
Account* acc = static_cast<Account*>(sender());
emit removePresence(acc->getName(), jid, name);
}

View File

@ -31,6 +31,8 @@ signals:
void removeContact(const QString& account, const QString& jid); void removeContact(const QString& account, const QString& jid);
void removeContact(const QString& account, const QString& jid, const QString& group); void removeContact(const QString& account, const QString& jid, const QString& group);
void changeContact(const QString& account, const QString& jid, const QString& name); void changeContact(const QString& account, const QString& jid, const QString& name);
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& account, const QString& jid, const QString& name);
public slots: public slots:
void start(); void start();
@ -57,6 +59,8 @@ private slots:
void onAccountRemoveContact(const QString& jid); void onAccountRemoveContact(const QString& jid);
void onAccountRemoveContact(const QString& jid, const QString& group); void onAccountRemoveContact(const QString& jid, const QString& group);
void onAccountChangeContact(const QString& jid, const QString& name); void onAccountChangeContact(const QString& jid, const QString& name);
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void onAccountRemovePresence(const QString& jid, const QString& name);
}; };
} }

View File

@ -13,6 +13,18 @@ enum ConnectionState {
error error
}; };
enum Availability {
online,
away,
extendedAway,
busy,
chatty,
offline
};
static const Availability availabilityHighest = offline;
static const Availability availabilityLowest = online;
static const std::deque<QString> ConnectionStateNames = {"Disconnected", "Connecting", "Connected", "Error"}; static const std::deque<QString> ConnectionStateNames = {"Disconnected", "Connecting", "Connected", "Error"};
static const std::deque<QString> ConnectionStateThemeIcons = {"network-disconnect", "view-refresh", "network-connect", "state-error"}; static const std::deque<QString> ConnectionStateThemeIcons = {"network-disconnect", "view-refresh", "network-connect", "state-error"};

View File

@ -41,6 +41,9 @@ int main(int argc, char *argv[])
QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&))); QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&)));
QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&, const QString&))); QObject::connect(squawk, SIGNAL(removeContact(const QString&, const QString&, const QString&)), &w, SLOT(removeContact(const QString&, const QString&, const QString&)));
QObject::connect(squawk, SIGNAL(changeContact(const QString&, const QString&, const QString&)), &w, SLOT(changeContact(const QString&, const QString&, const QString&))); QObject::connect(squawk, SIGNAL(changeContact(const QString&, const QString&, const QString&)), &w, SLOT(changeContact(const QString&, const QString&, const QString&)));
QObject::connect(squawk, SIGNAL(addPresence(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)),
&w, SLOT(addPresence(const QString&, const QString&, const QString&, const QMap<QString, QVariant>&)));
QObject::connect(squawk, SIGNAL(removePresence(const QString&, const QString&, const QString&)), &w, SLOT(removePresence(const QString&, const QString&, const QString&)));
coreThread->start(); coreThread->start();

View File

@ -18,6 +18,7 @@ set(squawkUI_SRC
models/item.cpp models/item.cpp
models/account.cpp models/account.cpp
models/contact.cpp models/contact.cpp
models/presence.cpp
) )
# Tell CMake to create the helloworld executable # Tell CMake to create the helloworld executable

View File

@ -17,7 +17,7 @@ void Models::Account::setState(int p_state)
{ {
if (state != p_state) { if (state != p_state) {
state = p_state; state = p_state;
emit changed(2); changed(2);
} }
} }
@ -45,7 +45,7 @@ void Models::Account::setLogin(const QString& p_login)
{ {
if (login != p_login) { if (login != p_login) {
login = p_login; login = p_login;
emit changed(3); changed(3);
} }
} }
@ -53,7 +53,7 @@ void Models::Account::setPassword(const QString& p_password)
{ {
if (password != p_password) { if (password != p_password) {
password = p_password; password = p_password;
emit changed(4); changed(4);
} }
} }
@ -61,7 +61,7 @@ void Models::Account::setServer(const QString& p_server)
{ {
if (server != p_server) { if (server != p_server) {
server = p_server; server = p_server;
emit changed(1); changed(1);
} }
} }

View File

@ -63,17 +63,19 @@ void Models::Accounts::addAccount(Account* account)
{ {
beginInsertRows(QModelIndex(), accs.size(), accs.size()); beginInsertRows(QModelIndex(), accs.size(), accs.size());
accs.push_back(account); accs.push_back(account);
connect(account, SIGNAL(changed(int)), this, SLOT(onAccountChanged(int))); connect(account, SIGNAL(childChanged(Item*, int, int)), this, SLOT(onAccountChanged(Item*, int, int)));
endInsertRows(); endInsertRows();
} }
void Models::Accounts::onAccountChanged(int column) void Models::Accounts::onAccountChanged(Item* item, int row, int col)
{ {
Account* acc = static_cast<Account*>(sender()); Account* acc = getAccount(row);
if (item != acc) {
return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that
}
if (column < columnCount(QModelIndex())) { if (col < columnCount(QModelIndex())) {
int row = acc->row(); emit dataChanged(createIndex(row, col, this), createIndex(row, col, this));
emit dataChanged(createIndex(row, column, this), createIndex(row, column, this));
} }
} }

View File

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

View File

@ -1,9 +1,11 @@
#include "contact.h" #include "contact.h"
#include <QDebug>
Models::Contact::Contact(const QMap<QString, QVariant>& data, Models::Item* parentItem): Models::Contact::Contact(const QMap<QString, QVariant>& data, Models::Item* parentItem):
Item(Item::contact, data, parentItem), Item(Item::contact, data, parentItem),
jid(data.value("jid").toString()), jid(data.value("jid").toString()),
state(data.value("state").toInt()) state(Shared::offline),
presences()
{ {
} }
@ -20,20 +22,20 @@ void Models::Contact::setJid(const QString p_jid)
{ {
if (jid != p_jid) { if (jid != p_jid) {
jid = p_jid; jid = p_jid;
emit changed(1); changed(1);
} }
} }
int Models::Contact::getState() const Shared::Availability Models::Contact::getState() const
{ {
return state; return state;
} }
void Models::Contact::setState(int p_state) void Models::Contact::setState(Shared::Availability p_state)
{ {
if (state != p_state) { if (state != p_state) {
state = p_state; state = p_state;
emit changed(2); changed(2);
} }
} }
@ -67,6 +69,78 @@ void Models::Contact::update(const QString& field, const QVariant& value)
} else if (field == "jid") { } else if (field == "jid") {
setJid(value.toString()); setJid(value.toString());
} else if (field == "state") { } else if (field == "state") {
setState(value.toInt()); unsigned int iState = value.toUInt();
if (iState <= Shared::availabilityHighest) {
Shared::Availability state = static_cast<Shared::Availability>(iState);
setState(state);
} else {
qDebug("An attempt to set wrong state to the contact");
}
} }
} }
void Models::Contact::addPresence(const QString& p_name, const QMap<QString, QVariant>& data)
{
QMap<QString, Presence*>::iterator itr = presences.find(p_name);
if (itr == presences.end()) {
Presence* pr = new Presence(data);
pr->setName(p_name);
presences.insert(p_name, pr);
appendChild(pr);
} else {
Presence* pr = itr.value();
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
pr->update(itr.key(), itr.value());
}
}
}
void Models::Contact::removePresence(const QString& name)
{
QMap<QString, Presence*>::iterator itr = presences.find(name);
if (itr == presences.end()) {
} else {
Presence* pr = itr.value();
presences.erase(itr);
removeChild(pr->row());
}
}
void Models::Contact::refresh()
{
QDateTime lastActivity;
Presence* presence = 0;
for (QMap<QString, Presence*>::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) {
Presence* pr = itr.value();
QDateTime la = pr->getLastActivity();
if (la > lastActivity) {
lastActivity = la;
presence = pr;
}
}
if (presence != 0) {
setState(presence->getAvailability());
}
}
void Models::Contact::_removeChild(int index)
{
Item::_removeChild(index);
refresh();
}
void Models::Contact::appendChild(Models::Item* child)
{
Item::appendChild(child);
refresh();
}
void Models::Contact::changed(int col)
{
Item::changed(col);
refresh();
}

View File

@ -2,6 +2,9 @@
#define MODELS_CONTACT_H #define MODELS_CONTACT_H
#include "item.h" #include "item.h"
#include "presence.h"
#include "../../global.h"
#include <QMap>
namespace Models { namespace Models {
@ -13,19 +16,32 @@ public:
~Contact(); ~Contact();
QString getJid() const; QString getJid() const;
void setJid(const QString p_jid);
int getState() const; Shared::Availability getState() const;
void setState(int p_state);
int columnCount() const override; int columnCount() const override;
QVariant data(int column) const override; QVariant data(int column) const override;
void update(const QString& field, const QVariant& value); void update(const QString& field, const QVariant& value);
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& name);
void appendChild(Models::Item * child) override;
protected:
void refresh();
void changed(int col) override;
void _removeChild(int index) override;
protected:
void setState(Shared::Availability p_state);
void setJid(const QString p_jid);
private: private:
QString jid; QString jid;
int state; Shared::Availability state;
QMap<QString, Presence*> presences;
}; };
} }

View File

@ -1,7 +1,5 @@
#include "item.h" #include "item.h"
using namespace Models;
Models::Item::Item(Type p_type, const QMap<QString, QVariant> &p_data, Item *p_parent): Models::Item::Item(Type p_type, const QMap<QString, QVariant> &p_data, Item *p_parent):
QObject(), QObject(),
type(p_type), type(p_type),
@ -24,13 +22,38 @@ void Models::Item::setName(const QString& p_name)
{ {
if (name != p_name) { if (name != p_name) {
name = p_name; name = p_name;
emit changed(0); changed(0);
} }
} }
void Models::Item::appendChild(Models::Item* child) void Models::Item::appendChild(Models::Item* child)
{ {
bool moving = false;
int oldRow = child->row();
int newRow = this->childCount();
if (child->parent != 0) {
moving = true;
emit childIsAboutToBeMoved(child->parent, oldRow, oldRow, this, newRow);
child->parent->_removeChild(oldRow);
} else {
emit childIsAboutToBeInserted(this, newRow, newRow);
}
childItems.push_back(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(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)));
QObject::connect(child, SIGNAL(childRemoved()), this, SIGNAL(childRemoved()));
QObject::connect(child, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)));
QObject::connect(child, SIGNAL(childMoved()), this, SIGNAL(childMoved()));
if (moving) {
emit childMoved();
} else {
emit childInserted();
}
} }
Models::Item * Models::Item::child(int row) Models::Item * Models::Item::child(int row)
@ -74,7 +97,6 @@ QString Models::Item::getName() const
return name; return name;
} }
QVariant Models::Item::data(int column) const QVariant Models::Item::data(int column) const
{ {
if (column != 0) { if (column != 0) {
@ -85,14 +107,31 @@ QVariant Models::Item::data(int column) const
void Models::Item::removeChild(int index) void Models::Item::removeChild(int index)
{ {
childItems.erase(childItems.begin() + index); emit childIsAboutToBeRemoved(this, index, index);
removeChild(index);
emit childRemoved();
} }
void Models::Item::setParent(Models::Item* p_parent) void Models::Item::_removeChild(int index)
{ {
parent = p_parent; Item* child = childItems[index];
QObject::connect(child, SIGNAL(childChanged(Item*, int, int)), this, SIGNAL(childChanged(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)));
QObject::connect(child, SIGNAL(childRemoved()), this, SIGNAL(childRemoved()));
QObject::connect(child, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)));
QObject::connect(child, SIGNAL(childMoved()), this, SIGNAL(childMoved()));
childItems.erase(childItems.begin() + index);
child->parent = 0;
} }
void Models::Item::changed(int col)
{
if (parent != 0) {
emit childChanged(this, row(), col);
}
}

View File

@ -17,6 +17,7 @@ class Item : public QObject{
group, group,
contact, contact,
conversation, conversation,
presence,
root root
}; };
@ -24,11 +25,17 @@ class Item : public QObject{
~Item(); ~Item();
signals: signals:
void changed(int col); void childChanged(Item* item, int row, int col);
void childIsAboutToBeInserted(Item* parent, int first, int last);
void childInserted();
void childIsAboutToBeRemoved(Item* parent, int first, int last);
void childRemoved();
void childIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
void childMoved();
public: public:
void appendChild(Item *child); virtual void appendChild(Item *child);
void removeChild(int index); virtual void removeChild(int index);
QString getName() const; QString getName() const;
void setName(const QString& name); void setName(const QString& name);
@ -38,16 +45,32 @@ class Item : public QObject{
virtual QVariant data(int column) const; virtual QVariant data(int column) const;
int row() const; int row() const;
Item *parentItem(); Item *parentItem();
void setParent(Item* p_parent);
const Type type; const Type type;
protected:
virtual void changed(int col);
virtual void _removeChild(int index);
protected: protected:
QString name; QString name;
std::deque<Item*> childItems; std::deque<Item*> childItems;
Item* parent; Item* parent;
protected slots:
}; };
} }
namespace Shared {
static const std::deque<QString> AvailabilityIcons = {
"im-user-online",
"im-user-away",
"im-user-away",
"im-user-busy",
"im-user-online",
"im-user-offline"
};
}
#endif // MODELS_ITEM_H #endif // MODELS_ITEM_H

119
ui/models/presence.cpp Normal file
View File

@ -0,0 +1,119 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "presence.h"
Models::Presence::Presence(const QMap<QString, QVariant>& data, Item* parentItem):
Item(Item::presence, data, parentItem),
availability(Shared::offline),
lastActivity(data.value("lastActivity").toDateTime()),
status(data.value("status").toString())
{
QMap<QString, QVariant>::const_iterator itr = data.find("availability");
if (itr != data.end()) {
setAvailability(itr.value().toUInt());
}
}
Models::Presence::~Presence()
{
}
int Models::Presence::columnCount() const
{
return 4;
}
QVariant Models::Presence::data(int column) const
{
switch (column) {
case 0:
return Item::data(column);
case 1:
return lastActivity;
case 2:
return availability;
case 3:
return status;
default:
return QVariant();
}
}
Shared::Availability Models::Presence::getAvailability() const
{
return availability;
}
QDateTime Models::Presence::getLastActivity() const
{
return lastActivity;
}
QString Models::Presence::getStatus() const
{
return status;
}
void Models::Presence::setAvailability(Shared::Availability p_avail)
{
if (availability != p_avail) {
availability = p_avail;
changed(2);
}
}
void Models::Presence::setAvailability(unsigned int avail)
{
if (avail <= Shared::availabilityHighest) {
Shared::Availability state = static_cast<Shared::Availability>(avail);
setAvailability(state);
} else {
qDebug("An attempt to set wrong state to the contact");
}
}
void Models::Presence::setLastActivity(const QDateTime& p_time)
{
if (lastActivity != p_time) {
lastActivity = p_time;
changed(1);
}
}
void Models::Presence::setStatus(const QString& p_state)
{
if (status != p_state) {
status = p_state;
changed(3);
}
}
void Models::Presence::update(const QString& key, const QVariant& value)
{
if (key == "name") {
setName(value.toString());
} else if (key == "status") {
setStatus(value.toString());
} else if (key == "availability") {
setAvailability(value.toUInt());
} else if (key == "lastActivity") {
setLastActivity(value.toDateTime());
}
}

58
ui/models/presence.h Normal file
View File

@ -0,0 +1,58 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef MODELS_PRESENCE_H
#define MODELS_PRESENCE_H
#include "item.h"
#include "../../global.h"
#include <QDateTime>
namespace Models {
class Presence : public Models::Item
{
Q_OBJECT
public:
explicit Presence(const QMap<QString, QVariant> &data, Item *parentItem = 0);
~Presence();
virtual int columnCount() const override;
virtual QVariant data(int column) const override;
Shared::Availability getAvailability() const;
void setAvailability(Shared::Availability p_avail);
void setAvailability(unsigned int avail);
QDateTime getLastActivity() const;
void setLastActivity(const QDateTime& p_time);
QString getStatus() const;
void setStatus(const QString& p_state);
void update(const QString& key, const QVariant& value);
private:
Shared::Availability availability;
QDateTime lastActivity;
QString status;
};
}
#endif // MODELS_PRESENCE_H

View File

@ -17,6 +17,13 @@ Models::Roster::Roster(QObject* parent):
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)),
this, this,
SLOT(onAccountDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&))); 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(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)));
connect(root, SIGNAL(childRemoved()), this, SLOT(onChildRemoved()));
connect(root, SIGNAL(childIsAboutToBeMoved(Item*, int, int, Item*, int)), this, SLOT(onChildIsAboutToBeMoved(Item*, int, int, Item*, int)));
connect(root, SIGNAL(childMoved()), this, SLOT(onChildMoved()));
} }
Models::Roster::~Roster() Models::Roster::~Roster()
@ -27,12 +34,10 @@ Models::Roster::~Roster()
void Models::Roster::addAccount(const QMap<QString, QVariant>& data) void Models::Roster::addAccount(const QMap<QString, QVariant>& data)
{ {
Account* acc = new Account(data, root); Account* acc = new Account(data);
beginInsertRows(QModelIndex(), root->childCount(), root->childCount());
root->appendChild(acc); root->appendChild(acc);
accounts.insert(std::make_pair(acc->getName(), acc)); accounts.insert(std::make_pair(acc->getName(), acc));
accountsModel->addAccount(acc); accountsModel->addAccount(acc);
endInsertRows();
} }
QVariant Models::Roster::data (const QModelIndex& index, int role) const QVariant Models::Roster::data (const QModelIndex& index, int role) const
@ -70,15 +75,12 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
break; break;
case Item::contact:{ case Item::contact:{
Contact* contact = static_cast<Contact*>(item); Contact* contact = static_cast<Contact*>(item);
int state = contact->getState(); result = QIcon::fromTheme(Shared::AvailabilityIcons[contact->getState()]);
switch (state) {
case 0:
result = QIcon::fromTheme("im-user-offline");
break;
case 1:
result = QIcon::fromTheme("im-user-online");
break;
} }
break;
case Item::presence:{
Presence* presence = static_cast<Presence*>(item);
result = QIcon::fromTheme(Shared::AvailabilityIcons[presence->getAvailability()]);
} }
break; break;
default: default:
@ -237,11 +239,9 @@ void Models::Roster::addGroup(const QString& account, const QString& name)
std::map<QString, Account*>::iterator itr = accounts.find(account); std::map<QString, Account*>::iterator itr = accounts.find(account);
if (itr != accounts.end()) { if (itr != accounts.end()) {
Account* acc = itr->second; Account* acc = itr->second;
Item* group = new Item(Item::group, {{"name", name}}, acc); Item* group = new Item(Item::group, {{"name", name}});
beginInsertRows(createIndex(acc->row(), 0, acc), acc->childCount(), acc->childCount());
acc->appendChild(group); acc->appendChild(group);
groups.insert(std::make_pair(id, group)); groups.insert(std::make_pair(id, group));
endInsertRows();
} else { } else {
qDebug() << "An attempt to add group " << name << " to non existing account " << account << ", skipping"; qDebug() << "An attempt to add group " << name << " to non existing account " << account << ", skipping";
} }
@ -300,24 +300,16 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
if (ca->getJid() == jid) { if (ca->getJid() == jid) {
qDebug() << "An attempt to add a already existing contact " << name << " to the group " << group << ", contact will be moved from ungrouped contacts of " << account; qDebug() << "An attempt to add a already existing contact " << name << " to the group " << group << ", contact will be moved from ungrouped contacts of " << account;
beginMoveRows(createIndex(acc->row(), 0, acc), i, i, createIndex(parent->row(), 0, parent), parent->childCount());
contact = ca;
acc->removeChild(i);
ca->setParent(parent);
parent->appendChild(ca); parent->appendChild(ca);
endMoveRows();
return; return;
} }
} }
} }
} }
contact = new Contact({{"name", name}, {"jid", jid}, {"state", 0}}, parent); contact = new Contact({{"name", name}, {"jid", jid}, {"state", 0}});
beginInsertRows(createIndex(parent->row(), 0, parent), parent->childCount(), parent->childCount());
parent->appendChild(contact); parent->appendChild(contact);
contacts.insert(std::make_pair(id, contact)); contacts.insert(std::make_pair(id, contact));
endInsertRows();
} }
void Models::Roster::removeGroup(const QString& account, const QString& name) void Models::Roster::removeGroup(const QString& account, const QString& name)
@ -332,9 +324,7 @@ void Models::Roster::removeGroup(const QString& account, const QString& name)
Item* parent = item->parentItem(); Item* parent = item->parentItem();
int row = item->row(); int row = item->row();
beginRemoveRows(createIndex(parent->row(), 0, parent), row, row);
parent->removeChild(row); parent->removeChild(row);
endRemoveRows();
std::deque<Contact*> toInsert; std::deque<Contact*> toInsert;
for (int i = 0; item->childCount() > 0; ++i) { for (int i = 0; item->childCount() > 0; ++i) {
@ -360,13 +350,10 @@ void Models::Roster::removeGroup(const QString& account, const QString& name)
if (toInsert.size() > 0) { if (toInsert.size() > 0) {
Account* acc = accounts.find("account")->second; Account* acc = accounts.find("account")->second;
beginInsertRows(createIndex(acc->row(), 0, acc), acc->childCount(), acc->childCount() + toInsert.size() - 1);
for (int i = 0; i < toInsert.size(); ++i) { for (int i = 0; i < toInsert.size(); ++i) {
Contact* cont = toInsert[i]; Contact* cont = toInsert[i];
cont->setParent(acc); acc->appendChild(cont); //TODO optimisation
acc->appendChild(cont);
} }
endInsertRows();
} }
delete item; delete item;
@ -396,10 +383,8 @@ void Models::Roster::removeContact(const QString& account, const QString& jid)
if (parent->type == Item::group && parent->childCount() == 1) { if (parent->type == Item::group && parent->childCount() == 1) {
toRemove.insert(parent->getName()); toRemove.insert(parent->getName());
} }
int row = contact->row();
beginRemoveRows(createIndex(parent->row(), 0, parent), row, row); parent->removeChild(contact->row());
parent->removeChild(row);
endRemoveRows();
delete contact; delete contact;
} }
@ -435,13 +420,84 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c
return; return;
} }
int row = cont->row(); gr->removeChild(cont->row());
beginRemoveRows(createIndex(gr->row(), 0, gr), row, row);
gr->removeChild(row);
endRemoveRows();
delete cont; delete cont;
if (gr->childCount() == 0) { if (gr->childCount() == 0) {
removeGroup(account, group); removeGroup(account, group);
} }
} }
void Models::Roster::onChildChanged(Models::Item* item, int row, int col)
{
QModelIndex index = createIndex(row, col, item);
emit dataChanged(index, index);
}
void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last)
{
int row = 0;
if (parent != root) {
row = parent->row();
beginInsertRows(createIndex(row, 0, parent), first, last);
} else {
beginInsertRows(QModelIndex(), first, last);
}
}
void Models::Roster::onChildIsAboutToBeMoved(Models::Item* source, int first, int last, Models::Item* destination, int newIndex)
{
int oldRow = 0;
if (source != root) {
oldRow = source->row();
}
int newRow = 0;
if (destination != root) {
newRow = destination->row();
}
beginMoveRows(createIndex(oldRow, 0, source), first, last, createIndex(newRow, 0, destination), newIndex);
}
void Models::Roster::onChildIsAboutToBeRemoved(Models::Item* parent, int first, int last)
{
int row = 0;
if (parent != root) {
row = parent->row();
}
beginRemoveRows(createIndex(row, 0, parent), first, last);
}
void Models::Roster::onChildInserted()
{
endInsertRows();
}
void Models::Roster::onChildMoved()
{
endMoveRows();
}
void Models::Roster::onChildRemoved()
{
endRemoveRows();
}
void Models::Roster::addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
{
ElId contactId(account, jid);
std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId);
std::multimap<ElId, Contact*>::iterator cEnd = contacts.upper_bound(contactId);
for (;cBeg != cEnd; ++cBeg) {
cBeg->second->addPresence(name, data);
}
}
void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name)
{
ElId contactId(account, jid);
std::multimap<ElId, Contact*>::iterator cBeg = contacts.lower_bound(contactId);
std::multimap<ElId, Contact*>::iterator cEnd = contacts.upper_bound(contactId);
for (;cBeg != cEnd; ++cBeg) {
cBeg->second->removePresence(name);
}
}

View File

@ -30,6 +30,8 @@ public:
void removeContact(const QString& account, const QString& jid, const QString& group); void removeContact(const QString& account, const QString& jid, const QString& group);
void removeContact(const QString& account, const QString& jid); void removeContact(const QString& account, const QString& jid);
void changeContact(const QString& account, const QString& jid, const QString& name); void changeContact(const QString& account, const QString& jid, const QString& name);
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& account, const QString& jid, const QString& name);
QVariant data ( const QModelIndex& index, int role ) const override; QVariant data ( const QModelIndex& index, int role ) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;
@ -49,6 +51,13 @@ private:
private slots: private slots:
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles); void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
void onChildChanged(Item* item, int row, int col);
void onChildIsAboutToBeInserted(Item* parent, int first, int last);
void onChildInserted();
void onChildIsAboutToBeRemoved(Item* parent, int first, int last);
void onChildRemoved();
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
void onChildMoved();
private: private:
class ElId { class ElId {

View File

@ -116,5 +116,15 @@ void Squawk::removeContact(const QString& account, const QString& jid, const QSt
rosterModel.removeContact(account, jid, group); rosterModel.removeContact(account, jid, group);
} }
void Squawk::addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
{
rosterModel.addPresence(account, jid, name, data);
}
void Squawk::removePresence(const QString& account, const QString& jid, const QString& name)
{
rosterModel.removePresence(account, jid, name);
}

View File

@ -38,6 +38,8 @@ public slots:
void removeContact(const QString& account, const QString& jid, const QString& group); void removeContact(const QString& account, const QString& jid, const QString& group);
void removeContact(const QString& account, const QString& jid); void removeContact(const QString& account, const QString& jid);
void changeContact(const QString& account, const QString& jid, const QString& name); void changeContact(const QString& account, const QString& jid, const QString& name);
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& account, const QString& jid, const QString& name);
private: private:
QScopedPointer<Ui::Squawk> m_ui; QScopedPointer<Ui::Squawk> m_ui;