diff --git a/CMakeLists.txt b/CMakeLists.txt index 1765da5..b3f5269 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ set(CMAKE_AUTOUIC ON) find_package(Qt5Widgets CONFIG REQUIRED) set(squawk_SRC main.cpp + signalcatcher.cpp ) # Tell CMake to create the helloworld executable diff --git a/core/account.cpp b/core/account.cpp index 169d9e1..f5b981a 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -40,7 +40,7 @@ void Core::Account::connect() void Core::Account::disconnect() { if (state != Shared::disconnected) { - client.disconnect(); + client.disconnectFromServer(); state = Shared::disconnected; emit connectionStateChanged(state); } @@ -71,4 +71,17 @@ QString Core::Account::getName() const return name; } +QString Core::Account::getLogin() const +{ + return login; +} +QString Core::Account::getPassword() const +{ + return password; +} + +QString Core::Account::getServer() const +{ + return server; +} diff --git a/core/account.h b/core/account.h index c4a211f..a68c423 100644 --- a/core/account.h +++ b/core/account.h @@ -21,6 +21,9 @@ public: Shared::ConnectionState getState() const; QString getName() const; + QString getLogin() const; + QString getServer() const; + QString getPassword() const; signals: void connectionStateChanged(int); diff --git a/core/squawk.cpp b/core/squawk.cpp index 6537ec1..896fca3 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -1,5 +1,6 @@ #include "squawk.h" #include +#include Core::Squawk::Squawk(QObject* parent): QObject(parent), @@ -14,12 +15,46 @@ Core::Squawk::~Squawk() Accounts::const_iterator itr = accounts.begin(); Accounts::const_iterator end = accounts.end(); for (; itr != end; ++itr) { - (*itr)->deleteLater(); + delete (*itr); } } +void Core::Squawk::stop() +{ + qDebug("Stopping squawk core.."); + + QSettings settings; + settings.beginGroup("core"); + settings.beginWriteArray("accounts"); + for (int i = 0; i < accounts.size(); ++i) { + settings.setArrayIndex(i); + Account* acc = accounts[i]; + settings.setValue("name", acc->getName()); + settings.setValue("server", acc->getServer()); + settings.setValue("login", acc->getLogin()); + settings.setValue("password", acc->getPassword()); + } + settings.endArray(); + settings.endGroup(); + + settings.sync(); + + emit quit(); +} + void Core::Squawk::start() { + qDebug("Starting squawk core.."); + + QSettings settings; + settings.beginGroup("core"); + int size = settings.beginReadArray("accounts"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + addAccount(settings.value("login").toString(), settings.value("server").toString(), settings.value("password").toString(), settings.value("name").toString()); + } + settings.endArray(); + settings.endGroup(); } void Core::Squawk::newAccountRequest(const QMap& map) diff --git a/core/squawk.h b/core/squawk.h index 2b4efe3..49fb2fd 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -22,11 +22,13 @@ public: ~Squawk(); signals: + void quit(); void newAccount(const QMap&); void accountConnectionStateChanged(const QString&, int); public slots: void start(); + void stop(); void newAccountRequest(const QMap& map); void connectAccount(const QString& account); void disconnectAccount(const QString& account); diff --git a/main.cpp b/main.cpp index a96fa03..d22aa02 100644 --- a/main.cpp +++ b/main.cpp @@ -1,12 +1,21 @@ #include "ui/squawk.h" #include "core/squawk.h" +#include "signalcatcher.h" #include #include #include +#include int main(int argc, char *argv[]) { QApplication app(argc, argv); + SignalCatcher sc(&app); + + QCoreApplication::setOrganizationName("Macaw"); + QCoreApplication::setOrganizationDomain("macaw.me"); + QCoreApplication::setApplicationName("Squawk"); + QCoreApplication::setApplicationVersion("0.0.1"); + Squawk w; w.show(); @@ -14,8 +23,10 @@ int main(int argc, char *argv[]) QThread* coreThread = new QThread(); squawk->moveToThread(coreThread); - QObject::connect(coreThread, SIGNAL(finished()), squawk, SLOT(deleteLater())); QObject::connect(coreThread, SIGNAL(started()), squawk, SLOT(start())); + QObject::connect(&app, SIGNAL(aboutToQuit()), squawk, SLOT(stop())); + QObject::connect(squawk, SIGNAL(quit()), coreThread, SLOT(quit())); + QObject::connect(coreThread, SIGNAL(finished()), squawk, SLOT(deleteLater())); QObject::connect(&w, SIGNAL(newAccountRequest(const QMap&)), squawk, SLOT(newAccountRequest(const QMap&))); QObject::connect(&w, SIGNAL(connectAccount(const QString&)), squawk, SLOT(connectAccount(const QString&))); @@ -24,10 +35,11 @@ int main(int argc, char *argv[]) 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); coreThread->start(); - return app.exec(); + int result = app.exec(); + coreThread->wait(500); //TODO hate doing that but settings for some reason don't get saved to the disk + + return result; } diff --git a/signalcatcher.cpp b/signalcatcher.cpp new file mode 100644 index 0000000..2fbea38 --- /dev/null +++ b/signalcatcher.cpp @@ -0,0 +1,59 @@ +#include "signalcatcher.h" +#include +#include +#include + +int SignalCatcher::sigintFd[2] = {0,0}; + +SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): + QObject(parent), + app(p_app) +{ + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd)) + { + qFatal("Couldn't create INT socketpair"); + } + + if (setup_unix_signal_handlers() != 0) + { + qFatal("Couldn't install unix handlers"); + } + + snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); + connect(snInt, SIGNAL(activated(int)), this, SLOT(handleSigInt())); +} + +SignalCatcher::~SignalCatcher() +{} + +void SignalCatcher::handleSigInt() +{ + snInt->setEnabled(false); + char tmp; + ::read(sigintFd[1], &tmp, sizeof(tmp)); + + app->quit(); + + snInt->setEnabled(true); +} + +void SignalCatcher::intSignalHandler(int unused) +{ + char a = 1; + ::write(sigintFd[0], &a, sizeof(a)); +} + +int SignalCatcher::setup_unix_signal_handlers() +{ + struct sigaction s_int; + + s_int.sa_handler = SignalCatcher::intSignalHandler; + sigemptyset(&s_int.sa_mask); + s_int.sa_flags = 0; + s_int.sa_flags |= SA_RESTART; + + if (sigaction(SIGINT, &s_int, 0) > 0) + return 1; + + return 0; +} diff --git a/signalcatcher.h b/signalcatcher.h new file mode 100644 index 0000000..cfff75d --- /dev/null +++ b/signalcatcher.h @@ -0,0 +1,30 @@ +#ifndef SIGNALCATCHER_H +#define SIGNALCATCHER_H + +#include +#include +#include + +class SignalCatcher: public QObject +{ + Q_OBJECT + +public: + SignalCatcher(QCoreApplication *p_app, QObject *parent = 0); + ~SignalCatcher(); + + static void intSignalHandler(int unused); + +public slots: + void handleSigInt(); + +private: + QCoreApplication *app; + static int sigintFd[2]; + + QSocketNotifier *snInt; + + static int setup_unix_signal_handlers(); +}; + +#endif // SIGNALCATCHER_H diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index c814207..d07db94 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -1,4 +1,5 @@ #include "roster.h" +#include using namespace Models; @@ -16,7 +17,7 @@ Models::Roster::~Roster() void Models::Roster::addAccount(const QMap& data) { - Item* acc = new Item(Item::account, data, root); + Account* acc = new Account(data, root); beginInsertRows(QModelIndex(), root->childCount(), root->childCount()); root->appendChild(acc); accounts.insert(std::make_pair(acc->name(), acc)); @@ -31,13 +32,34 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const QVariant result; + Item *item = static_cast(index.internalPointer()); switch (role) { case Qt::DisplayRole: { - Item *item = static_cast(index.internalPointer()); result = item->data(index.column()); } break; + case Qt::DecorationRole: + switch (item->type) { + case Item::account:{ + int state = item->data(1).toInt(); + switch (state) { + case Shared::disconnected: + result = QIcon::fromTheme("im-user-offline"); + break; + case Shared::connecting: + result = QIcon::fromTheme(Shared::ConnectionStateThemeIcons[state]); + break; + case Shared::connected: + result = QIcon::fromTheme("im-user-online"); + break; + } + } + break; + default: + break; + } + break; default: break; } @@ -45,6 +67,11 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const return result; } +void Models::Roster::Item::setName(const QString& name) +{ + itemData[0] = name; +} + int Models::Roster::columnCount (const QModelIndex& parent) const { if (parent.isValid()) { @@ -54,6 +81,26 @@ int Models::Roster::columnCount (const QModelIndex& parent) const } } +void Models::Roster::updateAccount(const QString& account, const QString& field, const QVariant& value) +{ + std::map::iterator itr = accounts.find(account); + if (itr != accounts.end()) { + Account* acc = itr->second; + if (field == "name") { + acc->setName(value.toString()); + accounts.erase(itr); + accounts.insert(std::make_pair(acc->name(), acc)); + int row = acc->row(); + emit dataChanged(createIndex(row, 0, acc), createIndex(row, 0, acc)); + } else if (field == "state") { + acc->setState(value.toInt()); + int row = acc->row(); + emit dataChanged(createIndex(row, 0, acc), createIndex(row, 0, acc)); + } + } +} + + Qt::ItemFlags Models::Roster::flags(const QModelIndex& index) const { if (!index.isValid()) { @@ -92,6 +139,10 @@ QModelIndex Models::Roster::parent (const QModelIndex& child) const } Item *childItem = static_cast(child.internalPointer()); + if (childItem == root) { + return QModelIndex(); + } + Item *parentItem = childItem->parentItem(); if (parentItem == root) { @@ -210,3 +261,20 @@ bool Models::Roster::ElId::operator <(const Models::Roster::ElId& other) const return account < other.account; } } + +Models::Roster::Account::Account(const QMap& data, Models::Roster::Item* parentItem): + Item(account, data, parentItem) +{ + itemData.push_back(data.value("state")); +} + +Models::Roster::Account::~Account() +{ +} + +void Models::Roster::Account::setState(int state) +{ + itemData[1] = state; +} + + diff --git a/ui/models/roster.h b/ui/models/roster.h index caec9b7..2db1c7d 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -4,6 +4,7 @@ #include #include #include +#include "../../global.h" namespace Models { @@ -12,24 +13,26 @@ class Roster : public QAbstractItemModel { class Item; class ElId; + class Account; Q_OBJECT public: Roster(QObject* parent = 0); ~Roster(); void addAccount(const QMap &data); + void updateAccount(const QString& account, const QString& field, const QVariant& value); - QVariant data ( const QModelIndex& index, int role ) const; + QVariant data ( const QModelIndex& index, int role ) const override; 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; + int columnCount ( const QModelIndex& parent ) const override; + int rowCount ( const QModelIndex& parent ) const override; + QModelIndex parent ( const QModelIndex& child ) const override; + QModelIndex index ( int row, int column, const QModelIndex& parent ) const override; private: Item* root; - std::map accounts; + std::map accounts; std::map elements; private: @@ -48,6 +51,7 @@ private: void appendChild(Item *child); QString name() const; + void setName(const QString& name); Item *child(int row); int childCount() const; @@ -58,12 +62,20 @@ private: const Type type; - private: + protected: std::deque childItems; std::deque itemData; Item* parent; }; + class Account : public Item { + public: + explicit Account(const QMap &data, Item *parentItem = 0); + ~Account(); + + void setState(int state); + }; + class ElId { public: ElId (const QString& p_account, const QString& p_name); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 3554d1c..952a7c6 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -107,6 +107,7 @@ void Squawk::accountConnectionStateChanged(const QString& account, int state) QMap* acc = itr->second; acc->insert("state", state); + rosterModel.updateAccount(account, "state", state); if (accounts != 0) { accounts->updateAccount(account, "state", state); }