From 3d947a0748ce3d526795bc1b3a5060082fd20bf8 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 1 Apr 2019 00:05:09 +0300 Subject: [PATCH] Roster model, account model, connection commena, connection report --- core/account.cpp | 61 ++++++++++++- core/account.h | 17 ++++ core/squawk.cpp | 36 +++++++- core/squawk.h | 10 ++ global.h | 15 +++ main.cpp | 3 + ui/CMakeLists.txt | 1 + ui/models/roster.cpp | 212 +++++++++++++++++++++++++++++++++++++++++++ ui/models/roster.h | 80 ++++++++++++++++ ui/squawk.cpp | 42 ++++++++- ui/squawk.h | 9 ++ ui/squawk.ui | 80 ++++++++++++++-- 12 files changed, 551 insertions(+), 15 deletions(-) create mode 100644 global.h create mode 100644 ui/models/roster.cpp create mode 100644 ui/models/roster.h diff --git a/core/account.cpp b/core/account.cpp index 9fd614b..1bf4664 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -8,12 +8,67 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& login(p_login), server(p_server), password(p_password), - client() + client(), + state(Shared::disconnected) { - + QObject::connect(&client, SIGNAL(connected()), this, SLOT(onClientConnected())); + QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); } Account::~Account() { - + } + +Shared::ConnectionState Core::Account::getState() const +{ + return state; +} + + +void Core::Account::connect() +{ + if (state == Shared::disconnected) { + client.connectToServer(login + "@" + server, password); + state = Shared::connecting; + emit connectionStateChanged(state); + } else { + qDebug("An attempt to connect an account which is already connected, skipping"); + } +} + +void Core::Account::disconnect() +{ + if (state != Shared::disconnected) { + client.disconnect(); + state = Shared::disconnected; + emit connectionStateChanged(state); + } +} + +void Core::Account::onClientConnected() +{ + if (state == Shared::connecting) { + state = Shared::connected; + emit connectionStateChanged(state); + } else { + qDebug("Something weird had happened - xmpp client reported about successful connection but account wasn't in connecting state"); + } +} + +void Core::Account::onClientDisonnected() +{ + if (state != Shared::disconnected) { + state = Shared::disconnected; + emit connectionStateChanged(state); + } else { + qDebug("Something weird had happened - xmpp client reported about being disconnection but account was already in disconnected state"); + } +} + +QString Core::Account::getName() const +{ + return name; +} + + diff --git a/core/account.h b/core/account.h index c6af16b..5e3c612 100644 --- a/core/account.h +++ b/core/account.h @@ -4,22 +4,39 @@ #include #include +#include "../global.h" namespace Core { class Account : public QObject { + Q_OBJECT public: Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent = 0); ~Account(); + void connect(); + void disconnect(); + + Shared::ConnectionState getState() const; + QString getName() const; + +signals: + void connectionStateChanged(int); + private: QString name; QString login; QString server; QString password; QXmppClient client; + Shared::ConnectionState state; + +private slots: + void onClientConnected(); + void onClientDisonnected(); + }; } diff --git a/core/squawk.cpp b/core/squawk.cpp index 667c885..1a8cfe0 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -3,7 +3,8 @@ Core::Squawk::Squawk(QObject* parent): QObject(parent), - accounts() + accounts(), + amap() { } @@ -35,13 +36,44 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const { Account* acc = new Account(login, server, password, name); accounts.push_back(acc); + amap.insert(std::make_pair(name, acc)); + + connect(acc, SIGNAL(connectionStateChanged(int)), this, SLOT(onAccountConnectionStateChanged(int))); QMap map = { {"login", login}, {"server", server}, {"name", name}, {"password", password}, - {"state", 0} + {"state", Shared::disconnected} }; emit newAccount(map); } + +void Core::Squawk::connectAccount(const QString& account) +{ + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug("An attempt to connect non existing account, skipping"); + return; + } + itr->second->connect(); +} + +void Core::Squawk::disconnectAccount(const QString& account) +{ + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug("An attempt to connect non existing account, skipping"); + return; + } + + itr->second->connect(); +} + +void Core::Squawk::onAccountConnectionStateChanged(int state) +{ + Account* acc = static_cast(sender()); + emit accountConnectionStateChanged(acc->getName(), state); +} + diff --git a/core/squawk.h b/core/squawk.h index 76c5d6e..2b4efe3 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -6,8 +6,10 @@ #include #include #include +#include #include "account.h" +#include "../global.h" namespace Core { @@ -21,18 +23,26 @@ public: signals: void newAccount(const QMap&); + void accountConnectionStateChanged(const QString&, int); public slots: void start(); void newAccountRequest(const QMap& map); + void connectAccount(const QString& account); + void disconnectAccount(const QString& account); private: typedef std::deque Accounts; + typedef std::map AccountsMap; Accounts accounts; + AccountsMap amap; private: void addAccount(const QString& login, const QString& server, const QString& password, const QString& name); + +private slots: + void onAccountConnectionStateChanged(int state); }; } diff --git a/global.h b/global.h new file mode 100644 index 0000000..5b7d0bd --- /dev/null +++ b/global.h @@ -0,0 +1,15 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +namespace Shared { + +enum ConnectionState { + disconnected, + connecting, + connected, + error +}; + +}; + +#endif // GLOBAL_H diff --git a/main.cpp b/main.cpp index ff710d7..a96fa03 100644 --- a/main.cpp +++ b/main.cpp @@ -18,8 +18,11 @@ int main(int argc, char *argv[]) QObject::connect(coreThread, SIGNAL(started()), squawk, SLOT(start())); QObject::connect(&w, SIGNAL(newAccountRequest(const QMap&)), squawk, SLOT(newAccountRequest(const QMap&))); + QObject::connect(&w, SIGNAL(connectAccount(const QString&)), squawk, SLOT(connectAccount(const QString&))); + QObject::connect(&w, SIGNAL(disconnectAccount(const QString&)), squawk, SLOT(disconnectAccount(const QString&))); QObject::connect(squawk, SIGNAL(newAccount(const QMap&)), &w, SLOT(newAccount(const QMap&))); + QObject::connect(squawk, SIGNAL(accountConnectionStateChanged(const QString&, int)), &w, SLOT(accountConnectionStateChanged(const QString&, int))); //QObject::connect(this, &Controller::operate, worker, &Worker::doWork); //QObject::connect(worker, &Worker::resultReady, this, &Controller::handleResults); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 7374c75..53826f4 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -14,6 +14,7 @@ set(squawkUI_SRC accounts.cpp account.cpp models/accounts.cpp + models/roster.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp new file mode 100644 index 0000000..c814207 --- /dev/null +++ b/ui/models/roster.cpp @@ -0,0 +1,212 @@ +#include "roster.h" + +using namespace Models; + +Models::Roster::Roster(QObject* parent): + QAbstractItemModel(parent), + root(0) +{ + root = new Item(Item::root, {{"name", "root"}}); +} + +Models::Roster::~Roster() +{ + delete root; +} + +void Models::Roster::addAccount(const QMap& data) +{ + Item* acc = new Item(Item::account, data, root); + beginInsertRows(QModelIndex(), root->childCount(), root->childCount()); + root->appendChild(acc); + accounts.insert(std::make_pair(acc->name(), acc)); + endInsertRows(); +} + +QVariant Models::Roster::data (const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + QVariant result; + + switch (role) { + case Qt::DisplayRole: + { + Item *item = static_cast(index.internalPointer()); + result = item->data(index.column()); + } + break; + default: + break; + } + + return result; +} + +int Models::Roster::columnCount (const QModelIndex& parent) const +{ + if (parent.isValid()) { + return static_cast(parent.internalPointer())->columnCount(); + } else { + return root->columnCount(); + } +} + +Qt::ItemFlags Models::Roster::flags(const QModelIndex& index) const +{ + if (!index.isValid()) { + return 0; + } + + return QAbstractItemModel::flags(index); +} + + +int Models::Roster::rowCount (const QModelIndex& parent) const +{ + Item *parentItem; + if (parent.column() > 0) { + return 0; + } + + if (!parent.isValid()) { + parentItem = root; + } else { + parentItem = static_cast(parent.internalPointer()); + } + + return parentItem->childCount(); +} + +QVariant Models::Roster::headerData(int section, Qt::Orientation orientation, int role) const +{ + return QVariant(); +} + +QModelIndex Models::Roster::parent (const QModelIndex& child) const +{ + if (!child.isValid()) { + return QModelIndex(); + } + + Item *childItem = static_cast(child.internalPointer()); + Item *parentItem = childItem->parentItem(); + + if (parentItem == root) { + return QModelIndex(); + } + + return createIndex(parentItem->row(), 0, parentItem); +} + +QModelIndex Models::Roster::index (int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + Item *parentItem; + + if (!parent.isValid()) { + parentItem = root; + } else { + parentItem = static_cast(parent.internalPointer()); + } + + Item *childItem = parentItem->child(row); + if (childItem) { + return createIndex(row, column, childItem); + } else { + return QModelIndex(); + } +} + + +Models::Roster::Item::Item(Type p_type, const QMap &p_data, Item *p_parent): + type(p_type), + childItems(), + itemData(), + parent(p_parent) +{ + itemData.push_back(p_data.value("name")); +} + +Models::Roster::Item::~Item() +{ + std::deque::const_iterator itr = childItems.begin(); + std::deque::const_iterator end = childItems.end(); + + for (;itr != end; ++itr) { + delete (*itr); + } +} + +void Models::Roster::Item::appendChild(Models::Roster::Item* child) +{ + childItems.push_back(child); +} + +Models::Roster::Item * Models::Roster::Item::child(int row) +{ + return childItems[row]; +} + +int Models::Roster::Item::childCount() const +{ + return childItems.size(); +} + +int Models::Roster::Item::row() const +{ + if (parent != 0) { + std::deque::const_iterator itr = parent->childItems.begin(); + std::deque::const_iterator end = parent->childItems.end(); + + for (int i = 0; itr != end; ++itr, ++i) { + if (*itr == this) { + return i; + } + } + } + + return 0; //TODO not sure how it helps, i copy-pasted it from the example +} + +Models::Roster::Item * Models::Roster::Item::parentItem() +{ + return parent; +} + +int Models::Roster::Item::columnCount() const +{ + return itemData.size(); +} + +QString Models::Roster::Item::name() const +{ + return itemData[0].toString(); +} + + +QVariant Models::Roster::Item::data(int column) const +{ + return itemData[column]; +} + +Models::Roster::ElId::ElId(const QString& p_account, const QString& p_name): + account(p_account), + name(p_name) +{ + +} + +bool Models::Roster::ElId::operator <(const Models::Roster::ElId& other) const +{ + if (account == other.account) { + return name < other.name; + } else { + return account < other.account; + } +} diff --git a/ui/models/roster.h b/ui/models/roster.h new file mode 100644 index 0000000..caec9b7 --- /dev/null +++ b/ui/models/roster.h @@ -0,0 +1,80 @@ +#ifndef MODELS_ROSTER_H +#define MODELS_ROSTER_H + +#include +#include +#include + +namespace Models +{ + +class Roster : public QAbstractItemModel +{ + class Item; + class ElId; + Q_OBJECT +public: + Roster(QObject* parent = 0); + ~Roster(); + + void addAccount(const QMap &data); + + QVariant data ( const QModelIndex& index, int role ) const; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + int columnCount ( const QModelIndex& parent ) const; + int rowCount ( const QModelIndex& parent ) const; + QModelIndex parent ( const QModelIndex& child ) const; + QModelIndex index ( int row, int column, const QModelIndex& parent ) const; + +private: + Item* root; + std::map accounts; + std::map elements; + +private: + class Item { + public: + enum Type { + account, + group, + contect, + conversation, + root + }; + + explicit Item(Type p_type, const QMap &data, Item *parentItem = 0); + ~Item(); + + void appendChild(Item *child); + QString name() const; + + Item *child(int row); + int childCount() const; + int columnCount() const; + QVariant data(int column) const; + int row() const; + Item *parentItem(); + + const Type type; + + private: + std::deque childItems; + std::deque itemData; + Item* parent; + }; + + class ElId { + public: + ElId (const QString& p_account, const QString& p_name); + + const QString account; + const QString name; + + bool operator < (const ElId& other) const; + }; +}; + +} + +#endif // MODELS_ROSTER_H diff --git a/ui/squawk.cpp b/ui/squawk.cpp index a32f35c..f16d412 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -5,11 +5,14 @@ Squawk::Squawk(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::Squawk), accounts(0), - accountsCache() + accountsCache(), + rosterModel() { m_ui->setupUi(this); + m_ui->roster->setModel(&rosterModel); - connect(m_ui->actionAccounts, SIGNAL(triggered()), this, SLOT(onAccounts())); + connect(m_ui->comboBox, SIGNAL(activated(int)), this, SLOT(onComboboxActivated(int))); + //m_ui->mainToolBar->addWidget(m_ui->comboBox); } Squawk::~Squawk() { @@ -57,7 +60,42 @@ void Squawk::onAccountsClosed(QObject* parent) void Squawk::newAccount(const QMap& account) { accountsCache.push_back(account); + rosterModel.addAccount(account); if (accounts != 0) { accounts->addAccount(account); } } + +void Squawk::onComboboxActivated(int index) +{ + if (index == 0) { + if (accountsCache.size() > 0) { + AC::const_iterator itr = accountsCache.begin(); + AC::const_iterator end = accountsCache.end(); + + for (; itr != end; ++itr) { + const QMap& acc = *itr; + if (acc.value("state").toInt() == Shared::disconnected) { + emit connectAccount(acc.value("name").toString()); + } + } + } else { + m_ui->comboBox->setCurrentIndex(1); + } + } else if (index == 1) { + AC::const_iterator itr = accountsCache.begin(); + AC::const_iterator end = accountsCache.end(); + + for (; itr != end; ++itr) { + const QMap& acc = *itr; + if (acc.value("state").toInt() != Shared::disconnected) { + emit disconnectAccount(acc.value("name").toString()); + } + } + } +} + +void Squawk::accountConnectionStateChanged(const QString& account, int state) +{ + +} diff --git a/ui/squawk.h b/ui/squawk.h index 72c1935..8ba47c7 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -7,6 +7,9 @@ #include #include "accounts.h" +#include "models/roster.h" + +#include "../global.h" namespace Ui { class Squawk; @@ -22,9 +25,12 @@ public: signals: void newAccountRequest(const QMap&); + void connectAccount(const QString&); + void disconnectAccount(const QString&); public slots: void newAccount(const QMap& account); + void accountConnectionStateChanged(const QString& account, int state); private: typedef std::deque> AC; @@ -32,6 +38,7 @@ private: Accounts* accounts; AC accountsCache; + Models::Roster rosterModel; protected: void closeEvent(QCloseEvent * event) override; @@ -39,6 +46,8 @@ protected: private slots: void onAccounts(); void onAccountsClosed(QObject* parent = 0); + void onComboboxActivated(int index); + }; #endif // SQUAWK_H diff --git a/ui/squawk.ui b/ui/squawk.ui index 30f1219..50c4549 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -6,41 +6,105 @@ 0 0 - 400 - 300 + 385 + 508 squawk - + + Qt::ToolButtonFollowStyle + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + Disconnected + + + 1 + + + + Available + + + + + + + + Disconnected + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Sunken + + + false + + + + + 0 0 - 400 + 385 27 - + Settings - + TopToolBarArea - true + false + - + + + .. + Accounts