diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 78ce738..87ecf6a 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -17,6 +17,7 @@ set(squawkUI_SRC models/roster.cpp models/item.cpp models/account.cpp + models/contact.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp new file mode 100644 index 0000000..cb7c75a --- /dev/null +++ b/ui/models/contact.cpp @@ -0,0 +1,72 @@ +#include "contact.h" + +Models::Contact::Contact(const QMap& data, Models::Item* parentItem): + Item(Item::contact, data, parentItem), + jid(data.value("jid").toString()), + state(data.value("state").toInt()) +{ +} + +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; + emit changed(1); + } +} + +int Models::Contact::getState() const +{ + return state; +} + +void Models::Contact::setState(int p_state) +{ + if (state != p_state) { + state = p_state; + emit changed(2); + } +} + +int Models::Contact::columnCount() const +{ + return 3; +} + +QVariant Models::Contact::data(int column) const +{ + switch (column) { + case 0: + if (name == "") { + return jid; + } else { + return Item::data(column); + } + case 1: + return jid; + case 2: + return state; + default: + return QVariant(); + } +} + +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 == "state") { + setState(value.toInt()); + } +} diff --git a/ui/models/contact.h b/ui/models/contact.h new file mode 100644 index 0000000..5c95e61 --- /dev/null +++ b/ui/models/contact.h @@ -0,0 +1,33 @@ +#ifndef MODELS_CONTACT_H +#define MODELS_CONTACT_H + +#include "item.h" + +namespace Models { + +class Contact : public Item +{ + Q_OBJECT +public: + Contact(const QMap &data, Item *parentItem = 0); + ~Contact(); + + QString getJid() const; + void setJid(const QString p_jid); + + int getState() const; + void setState(int p_state); + + int columnCount() const override; + QVariant data(int column) const override; + + void update(const QString& field, const QVariant& value); + +private: + QString jid; + int state; +}; + +} + +#endif // MODELS_CONTACT_H diff --git a/ui/models/item.cpp b/ui/models/item.cpp index 7a6411b..630331c 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -82,3 +82,17 @@ QVariant Models::Item::data(int column) const } return name; } + +void Models::Item::removeChild(int index) +{ + childItems.erase(childItems.begin() + index); +} + +void Models::Item::setParent(Models::Item* p_parent) +{ + parent = p_parent; +} + + + + diff --git a/ui/models/item.h b/ui/models/item.h index 388fdfb..81a6370 100644 --- a/ui/models/item.h +++ b/ui/models/item.h @@ -28,6 +28,7 @@ class Item : public QObject{ public: void appendChild(Item *child); + void removeChild(int index); QString getName() const; void setName(const QString& name); @@ -37,6 +38,7 @@ class Item : public QObject{ virtual QVariant data(int column) const; int row() const; Item *parentItem(); + void setParent(Item* p_parent); const Type type; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index f3f9cda..69fe040 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -1,6 +1,7 @@ #include "roster.h" #include #include +#include using namespace Models; @@ -10,8 +11,7 @@ Models::Roster::Roster(QObject* parent): root(new Item(Item::root, {{"name", "root"}})), accounts(), groups(), - contacts(), - elements() + contacts() { connect(accountsModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)), @@ -68,10 +68,40 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const } } break; + case Item::contact:{ + Contact* contact = static_cast(item); + int state = contact->getState(); + switch (state) { + case 0: + result = QIcon::fromTheme("im-user-offline"); + break; + case 1: + result = QIcon::fromTheme("im-user-online"); + break; + } + } + break; default: break; } break; + case Qt::FontRole: + switch (item->type) { + case Item::account:{ + QFont font; + font.setBold(true); + result = font; + } + break; + case Item::group:{ + QFont font; + font.setItalic(true); + result = font; + } + break; + default: + break; + } default: break; } @@ -198,14 +228,19 @@ void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelInd void Models::Roster::addGroup(const QString& account, const QString& name) { + ElId id(account, name); + std::map::const_iterator gItr = groups.find(id); + if (gItr != groups.end()) { + qDebug() << "An attempt to add group " << name << " to account " << account <<" which already exists there, skipping"; + return; + } std::map::iterator itr = accounts.find(account); if (itr != accounts.end()) { Account* acc = itr->second; Item* group = new Item(Item::group, {{"name", name}}, acc); beginInsertRows(createIndex(acc->row(), 0, acc), acc->childCount(), acc->childCount()); acc->appendChild(group); - groups.insert(std::make_pair(name, group)); - elements.insert({{account, name}, group}); + groups.insert(std::make_pair(id, group)); endInsertRows(); } else { qDebug() << "An attempt to add group " << name << " to non existing account " << account << ", skipping"; @@ -215,35 +250,125 @@ void Models::Roster::addGroup(const QString& account, const QString& name) void Models::Roster::addContact(const QString& account, const QString& jid, const QString& name, const QString& group) { Item* parent; - if (group == "") { + Account* acc; + Contact* contact; + ElId id(account, jid); + + { std::map::iterator itr = accounts.find(account); if (itr == accounts.end()) { qDebug() << "An attempt to add a contact " << name << " to non existing account " << account << ", skipping"; return; } - parent = itr->second; + acc = itr->second; + } + + if (group == "") { + std::multimap::iterator itr = contacts.lower_bound(id); + std::multimap::iterator eItr = contacts.upper_bound(id); + while (itr != eItr) { + if (itr->second->parentItem() == acc) { + qDebug() << "An attempt to add a contact " << name << " ungrouped to non the account " << account << " for the second time, skipping"; + return; + } + } + parent = acc; } else { - std::map::iterator itr = groups.find(group); + std::map::iterator itr = groups.find({account, group}); if (itr == groups.end()) { qDebug() << "An attempt to add a contact " << name << " to non existing group " << group << ", skipping"; return; } + parent = itr->second; + + for (int i = 0; i < parent->childCount(); ++i) { + Item* item = parent->child(i); + if (item->type == Item::contact) { + Contact* ca = static_cast(item); + if (ca->getJid() == jid) { + qDebug() << "An attempt to add a contact " << name << " to the group " << group << " for the second time, skipping"; + return; + } + } + } + + for (int i = 0; i < acc->childCount(); ++i) { + Item* item = acc->child(i); + if (item->type == Item::contact) { + Contact* ca = static_cast(item); + 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; + + 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); + endMoveRows(); + return; + } + } + } + } - - QString sName = name; - if (sName == "") { - sName = jid; - } - Item* contact = new Item(Item::contact, {{"name", sName}}, parent); + contact = new Contact({{"name", name}, {"jid", jid}, {"state", 0}}, parent); beginInsertRows(createIndex(parent->row(), 0, parent), parent->childCount(), parent->childCount()); parent->appendChild(contact); - contacts.insert(std::make_pair(jid, contact)); - elements.insert({{account, jid}, contact}); + contacts.insert(std::make_pair(id, contact)); endInsertRows(); + } void Models::Roster::removeGroup(const QString& account, const QString& name) { + ElId id(account, name); + std::map::const_iterator gItr = groups.find(id); + if (gItr == groups.end()) { + qDebug() << "An attempt to remove group " << name << " from account " << account <<" which doesn't exist there, skipping"; + return; + } + Item* item = gItr->second; + Item* parent = item->parentItem(); + int row = item->row(); + + beginRemoveRows(createIndex(parent->row(), 1, parent), row, row); + parent->removeChild(row); + endRemoveRows(); + + std::deque toInsert; + for (int i = 0; item->childCount() > 0; ++i) { + Contact* cont = static_cast(item->child(0)); + item->removeChild(0); + + std::multimap::iterator cBeg = contacts.lower_bound({account, cont->getJid()}); + std::multimap::iterator cEnd = contacts.upper_bound({account, cont->getJid()}); + + int count = std::distance(cBeg, cEnd); + if (count > 1) { + for (; cBeg != cEnd; ++count, ++cBeg) { + if (cBeg->second == cont) { + delete cont; + contacts.erase(cBeg); + break; + } + } + } else { + toInsert.push_back(cont); + } + } + + if (toInsert.size() > 0) { + 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) { + Contact* cont = toInsert[i]; + cont->setParent(acc); + acc->appendChild(cont); + } + endInsertRows(); + } + + delete item; } diff --git a/ui/models/roster.h b/ui/models/roster.h index 2d38a7b..7f511c0 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -9,6 +9,7 @@ #include "accounts.h" #include "item.h" #include "account.h" +#include "contact.h" namespace Models { @@ -40,9 +41,8 @@ public: private: Item* root; std::map accounts; - std::map groups; - std::map contacts; - std::map elements; + std::map groups; + std::multimap contacts; private slots: void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles);