diff --git a/core/account.cpp b/core/account.cpp index 2be816e..d4b4eec 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -346,8 +346,12 @@ void Core::Account::setServer(const QString& p_server) Shared::Availability Core::Account::getAvailability() const { - QXmppPresence::AvailableStatusType pres = presence.availableStatusType(); - return static_cast(pres); //TODO that's a shame! gotta do something about it + if (state == Shared::connected) { + QXmppPresence::AvailableStatusType pres = presence.availableStatusType(); + return static_cast(pres); //they are compatible; + } else { + return Shared::offline; + } } void Core::Account::setAvailability(Shared::Availability avail) diff --git a/global.h b/global.h index 7152180..77306f9 100644 --- a/global.h +++ b/global.h @@ -21,6 +21,7 @@ enum Availability { extendedAway, busy, chatty, + invisible, offline }; @@ -42,14 +43,15 @@ static const std::deque connectionStateNames = {"Disconnected", "Connec static const std::deque connectionStateThemeIcons = {"network-disconnect", "view-refresh", "network-connect", "state-error"}; static const std::deque availabilityThemeIcons = { - "im-user-online", - "im-user-away", - "im-user-away", - "im-user-busy", - "im-user-online", - "im-user-offline" + "user-online", + "user-away", + "user-away-extended", + "user-busy", + "user-online", + "user-invisible", + "user-offline" }; -static const std::deque availabilityNames = {"Online", "Away", "Absent", "Busy", "Chatty", "Offline"}; +static const std::deque availabilityNames = {"Online", "Away", "Absent", "Busy", "Chatty", "Invisible", "Offline"}; static const std::deque subscriptionStateThemeIcons = {"edit-none", "arrow-down-double", "arrow-up-double", "dialog-ok", "question"}; static const std::deque subscriptionStateNames = {"None", "From", "To", "Both", "Unknown"}; diff --git a/main.cpp b/main.cpp index 28c0779..5ab4156 100644 --- a/main.cpp +++ b/main.cpp @@ -20,6 +20,18 @@ int main(int argc, char *argv[]) QCoreApplication::setApplicationName("Squawk"); QCoreApplication::setApplicationVersion("0.0.1"); + QIcon icon; + icon.addFile(":images/logo.svg", QSize(16, 16)); + icon.addFile(":images/logo.svg", QSize(24, 24)); + icon.addFile(":images/logo.svg", QSize(32, 32)); + icon.addFile(":images/logo.svg", QSize(48, 48)); + icon.addFile(":images/logo.svg", QSize(64, 64)); + icon.addFile(":images/logo.svg", QSize(96, 96)); + icon.addFile(":images/logo.svg", QSize(128, 128)); + icon.addFile(":images/logo.svg", QSize(256, 256)); + icon.addFile(":images/logo.svg", QSize(512, 512)); + QApplication::setWindowIcon(icon); + Squawk w; w.show(); diff --git a/resources/images/fallback/dark/big/absent.svg b/resources/images/fallback/dark/big/absent.svg new file mode 100644 index 0000000..d1e2157 --- /dev/null +++ b/resources/images/fallback/dark/big/absent.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/resources/images/fallback/dark/big/away.svg b/resources/images/fallback/dark/big/away.svg new file mode 100644 index 0000000..658f75f --- /dev/null +++ b/resources/images/fallback/dark/big/away.svg @@ -0,0 +1,52 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/resources/images/fallback/dark/big/busy.svg b/resources/images/fallback/dark/big/busy.svg new file mode 100644 index 0000000..7fe95ca --- /dev/null +++ b/resources/images/fallback/dark/big/busy.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/images/fallback/dark/big/invisible.svg b/resources/images/fallback/dark/big/invisible.svg new file mode 100644 index 0000000..53b94da --- /dev/null +++ b/resources/images/fallback/dark/big/invisible.svg @@ -0,0 +1,17 @@ + + + + + + + diff --git a/resources/images/fallback/dark/big/offline.svg b/resources/images/fallback/dark/big/offline.svg new file mode 100644 index 0000000..6867e30 --- /dev/null +++ b/resources/images/fallback/dark/big/offline.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/resources/images/fallback/dark/big/online.svg b/resources/images/fallback/dark/big/online.svg new file mode 100644 index 0000000..04f62ec --- /dev/null +++ b/resources/images/fallback/dark/big/online.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/resources/images/fallback/dark/big/user.svg b/resources/images/fallback/dark/big/user.svg new file mode 100644 index 0000000..235adfc --- /dev/null +++ b/resources/images/fallback/dark/big/user.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/absent.svg b/resources/images/fallback/dark/small/absent.svg new file mode 100644 index 0000000..0710f4f --- /dev/null +++ b/resources/images/fallback/dark/small/absent.svg @@ -0,0 +1,9 @@ + + + + diff --git a/resources/images/fallback/dark/small/away.svg b/resources/images/fallback/dark/small/away.svg new file mode 100644 index 0000000..a7334a9 --- /dev/null +++ b/resources/images/fallback/dark/small/away.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/busy.svg b/resources/images/fallback/dark/small/busy.svg new file mode 100644 index 0000000..2665d86 --- /dev/null +++ b/resources/images/fallback/dark/small/busy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/fallback/dark/small/invisible.svg b/resources/images/fallback/dark/small/invisible.svg new file mode 100644 index 0000000..d293fca --- /dev/null +++ b/resources/images/fallback/dark/small/invisible.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/resources/images/fallback/dark/small/offline.svg b/resources/images/fallback/dark/small/offline.svg new file mode 100644 index 0000000..1a516a2 --- /dev/null +++ b/resources/images/fallback/dark/small/offline.svg @@ -0,0 +1,15 @@ + + + + + + + diff --git a/resources/images/fallback/dark/small/online.svg b/resources/images/fallback/dark/small/online.svg new file mode 100644 index 0000000..1acb801 --- /dev/null +++ b/resources/images/fallback/dark/small/online.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/fallback/dark/small/user.svg b/resources/images/fallback/dark/small/user.svg new file mode 100644 index 0000000..cdb5eae --- /dev/null +++ b/resources/images/fallback/dark/small/user.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/big/absent.svg b/resources/images/fallback/light/big/absent.svg new file mode 100644 index 0000000..1231521 --- /dev/null +++ b/resources/images/fallback/light/big/absent.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/resources/images/fallback/light/big/away.svg b/resources/images/fallback/light/big/away.svg new file mode 100644 index 0000000..d99de84 --- /dev/null +++ b/resources/images/fallback/light/big/away.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/fallback/light/big/busy.svg b/resources/images/fallback/light/big/busy.svg new file mode 100644 index 0000000..ce26031 --- /dev/null +++ b/resources/images/fallback/light/big/busy.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/fallback/light/big/invisible.svg b/resources/images/fallback/light/big/invisible.svg new file mode 100644 index 0000000..3ccea16 --- /dev/null +++ b/resources/images/fallback/light/big/invisible.svg @@ -0,0 +1,17 @@ + + + + + + + diff --git a/resources/images/fallback/light/big/offline.svg b/resources/images/fallback/light/big/offline.svg new file mode 100644 index 0000000..c01c87f --- /dev/null +++ b/resources/images/fallback/light/big/offline.svg @@ -0,0 +1,17 @@ + + + + + + + diff --git a/resources/images/fallback/light/big/online.svg b/resources/images/fallback/light/big/online.svg new file mode 100644 index 0000000..0bc4758 --- /dev/null +++ b/resources/images/fallback/light/big/online.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/fallback/light/big/user.svg b/resources/images/fallback/light/big/user.svg new file mode 100644 index 0000000..688167a --- /dev/null +++ b/resources/images/fallback/light/big/user.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/small/absent.svg b/resources/images/fallback/light/small/absent.svg new file mode 100644 index 0000000..0710f4f --- /dev/null +++ b/resources/images/fallback/light/small/absent.svg @@ -0,0 +1,9 @@ + + + + diff --git a/resources/images/fallback/light/small/away.svg b/resources/images/fallback/light/small/away.svg new file mode 100644 index 0000000..a7334a9 --- /dev/null +++ b/resources/images/fallback/light/small/away.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/fallback/light/small/busy.svg b/resources/images/fallback/light/small/busy.svg new file mode 100644 index 0000000..2665d86 --- /dev/null +++ b/resources/images/fallback/light/small/busy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/fallback/light/small/invisible.svg b/resources/images/fallback/light/small/invisible.svg new file mode 100644 index 0000000..1ec9f2d --- /dev/null +++ b/resources/images/fallback/light/small/invisible.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/resources/images/fallback/light/small/offline.svg b/resources/images/fallback/light/small/offline.svg new file mode 100644 index 0000000..dad08fd --- /dev/null +++ b/resources/images/fallback/light/small/offline.svg @@ -0,0 +1,15 @@ + + + + + + + diff --git a/resources/images/fallback/light/small/online.svg b/resources/images/fallback/light/small/online.svg new file mode 100644 index 0000000..1acb801 --- /dev/null +++ b/resources/images/fallback/light/small/online.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/fallback/light/small/user.svg b/resources/images/fallback/light/small/user.svg new file mode 100644 index 0000000..df21ae2 --- /dev/null +++ b/resources/images/fallback/light/small/user.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/logo.svg b/resources/images/logo.svg index 916ca13..5a17f93 100644 --- a/resources/images/logo.svg +++ b/resources/images/logo.svg @@ -1,30 +1,214 @@ - - + - - - - - - - - - - - - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - diff --git a/resources/resources.qrc b/resources/resources.qrc index 8d53a6f..a275128 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,5 +1,37 @@ images/logo.svg + + images/fallback/dark/big/absent.svg + images/fallback/dark/big/away.svg + images/fallback/dark/big/busy.svg + images/fallback/dark/big/invisible.svg + images/fallback/dark/big/offline.svg + images/fallback/dark/big/online.svg + images/fallback/dark/big/user.svg + + images/fallback/dark/small/absent.svg + images/fallback/dark/small/away.svg + images/fallback/dark/small/busy.svg + images/fallback/dark/small/invisible.svg + images/fallback/dark/small/offline.svg + images/fallback/dark/small/online.svg + images/fallback/dark/small/user.svg + + images/fallback/light/big/absent.svg + images/fallback/light/big/away.svg + images/fallback/light/big/busy.svg + images/fallback/light/big/invisible.svg + images/fallback/light/big/offline.svg + images/fallback/light/big/online.svg + images/fallback/light/big/user.svg + + images/fallback/light/small/absent.svg + images/fallback/light/small/away.svg + images/fallback/light/small/busy.svg + images/fallback/light/small/invisible.svg + images/fallback/light/small/offline.svg + images/fallback/light/small/online.svg + images/fallback/light/small/user.svg diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index e36a101..bcdc838 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -20,6 +20,7 @@ set(squawkUI_SRC models/account.cpp models/contact.cpp models/presence.cpp + models/group.cpp conversation.cpp messageline.cpp newcontact.cpp diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 53c68a5..8fedbc2 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -243,7 +243,16 @@ void Models::Contact::addMessage(const Shared::Message& data) if (res.size() > 0) { QMap::iterator itr = presences.find(res); if (itr == presences.end()) { - qDebug() << "An attempt to add message to the roster to the unknown resource " << res << " of contact " << jid << " in account " << getAccountName() << ", skipping"; + // this is actually the place when I can spot someone's invisible presence, and there is nothing criminal in it, cuz the sender sent us a message + // therefore he have revealed himself + // the only issue is to find out when the sender is gone offline + Presence* pr = new Presence({}); + pr->setName(res); + pr->setAvailability(Shared::invisible); + pr->setLastActivity(QDateTime::currentDateTime()); + presences.insert(res, pr); + appendChild(pr); + pr->addMessage(data); return; } itr.value()->addMessage(data); diff --git a/ui/models/group.cpp b/ui/models/group.cpp new file mode 100644 index 0000000..61098f1 --- /dev/null +++ b/ui/models/group.cpp @@ -0,0 +1,105 @@ +/* + * + * Copyright (C) 2019 Юрий Губич + * + * 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 . + */ + +#include "group.h" +#include "contact.h" + +Models::Group::Group(const QMap& data, Models::Item* parentItem): + Item(group, data, parentItem), + unreadMessages(0) +{ +} + +Models::Group::~Group() +{ +} + +void Models::Group::appendChild(Models::Item* child) +{ + Item::appendChild(child); + connect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + changed(1); + refresh(); +} + +int Models::Group::columnCount() const +{ + return 3; +} + +QVariant Models::Group::data(int column) const +{ + switch (column) { + case 0: + return getName(); + case 1: + return QVariant((unsigned int)childItems.size()); + case 2: + return unreadMessages; + default: + return QVariant(); + } +} + +void Models::Group::_removeChild(int index) +{ + Item* child = childItems[index]; + disconnect(child, SIGNAL(childChanged(Models::Item*, int, int)), this, SLOT(refresh())); + Item::_removeChild(index); + changed(1); + refresh(); +} + +unsigned int Models::Group::getUnreadMessages() const +{ + return unreadMessages; +} + +void Models::Group::setUnreadMessages(unsigned int amount) +{ + if (unreadMessages != amount) { + unreadMessages = amount; + changed(2); + } +} + +void Models::Group::refresh() +{ + unsigned int newAmount(0); + + for (std::deque::const_iterator itr = childItems.begin(), end = childItems.end(); itr != end; ++itr) { + Models::Contact* cnt = static_cast(*itr); + newAmount += cnt->getMessagesCount(); + } + + setUnreadMessages(newAmount); +} + +unsigned int Models::Group::getOnlineContacts() const +{ + unsigned int amount(0); + + for (std::deque::const_iterator itr = childItems.begin(), end = childItems.end(); itr != end; ++itr) { + Models::Contact* cnt = static_cast(*itr); + if (cnt->getAvailability() != Shared::offline) { + ++amount; + } + } + + return amount; +} diff --git a/ui/models/group.h b/ui/models/group.h new file mode 100644 index 0000000..fe1bb0d --- /dev/null +++ b/ui/models/group.h @@ -0,0 +1,54 @@ +/* + * + * Copyright (C) 2019 Юрий Губич + * + * 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 . + */ + +#ifndef MODELS_GROUP_H +#define MODELS_GROUP_H + +#include "item.h" + +namespace Models { + +class Group : public Models::Item +{ + Q_OBJECT +public: + Group(const QMap &data, Item *parentItem = 0); + ~Group(); + + void appendChild(Models::Item* child) override; + int columnCount() const override; + QVariant data(int column) const override; + + unsigned int getUnreadMessages() const; + unsigned int getOnlineContacts() const; + +protected: + void _removeChild(int index) override; + void setUnreadMessages(unsigned int amount); + +private slots: + void refresh(); + +private: + unsigned int unreadMessages; + +}; + +} + +#endif // MODELS_GROUP_H diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index fa5eb9e..bc8d827 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -52,7 +52,24 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: { - result = item->data(index.column()); + switch (item->type) { + case Item::group: { + Group* gr = static_cast(item); + QString str(""); + + str += gr->getName(); + unsigned int amount = gr->getUnreadMessages(); + if (amount > 0) { + str += QString(" (") + "New messages" + ")"; + } + + result = str; + } + break; + default: + result = item->data(index.column()); + break; + } } break; case Qt::DecorationRole: @@ -98,12 +115,12 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const switch (item->type) { case Item::account: { Account* acc = static_cast(item); - result = QString(Shared::connectionStateNames[acc->getAvailability()]); + result = QString(Shared::availabilityNames[acc->getAvailability()]); } break; case Item::contact: { Contact* contact = static_cast(item); - QString str = QString(""); + QString str(""); int mc = contact->getMessagesCount(); if (mc > 0) { str += QString("New messages: ") + std::to_string(mc).c_str() + "\n"; @@ -128,7 +145,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const break; case Item::presence: { Presence* contact = static_cast(item); - QString str = QString(""); + QString str(""); int mc = contact->getMessagesCount(); if (mc > 0) { str += QString("New messages: ") + std::to_string(mc).c_str() + "\n"; @@ -140,6 +157,18 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const str += "\nStatus: " + s; } + result = str; + } + break; + case Item::group: { + Group* gr = static_cast(item); + unsigned int count = gr->getUnreadMessages(); + QString str(""); + if (count > 0) { + str += QString("New messages: ") + std::to_string(count).c_str() + "\n"; + } + str += QString("Online contacts: ") + std::to_string(gr->getOnlineContacts()).c_str() + "\n"; + str += QString("Total contacts: ") + std::to_string(gr->childCount()).c_str(); result = str; } break; @@ -275,7 +304,7 @@ 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); + 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; @@ -283,7 +312,7 @@ void Models::Roster::addGroup(const QString& account, const QString& name) std::map::iterator itr = accounts.find(account); if (itr != accounts.end()) { Account* acc = itr->second; - Item* group = new Item(Item::group, {{"name", name}}); + Group* group = new Group({{"name", name}}); acc->appendChild(group); groups.insert(std::make_pair(id, group)); } else { @@ -319,7 +348,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons } parent = acc; } else { - std::map::iterator itr = groups.find({account, group}); + std::map::iterator itr = groups.find({account, group}); if (itr == groups.end()) { qDebug() << "An attempt to add a contact " << jid << " to non existing group " << group << ", skipping"; return; @@ -360,12 +389,12 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons void Models::Roster::removeGroup(const QString& account, const QString& name) { ElId id(account, name); - std::map::const_iterator gItr = groups.find(id); + 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; + Group* item = gItr->second; Item* parent = item->parentItem(); int row = item->row(); @@ -448,12 +477,12 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c ElId contactId(account, jid); ElId groupId(account, group); - std::map::iterator gItr = groups.find(groupId); + std::map::iterator gItr = groups.find(groupId); if (gItr == groups.end()) { qDebug() << "An attempt to remove contact " << jid << " from non existing group " << group << " of account " << account <<", skipping"; return; } - Item* gr = gItr->second; + Group* gr = gItr->second; Contact* cont = 0; std::multimap::iterator cBeg = contacts.lower_bound(contactId); @@ -598,10 +627,10 @@ void Models::Roster::removeAccount(const QString& account) } } - std::map::const_iterator gItr = groups.begin(); + std::map::const_iterator gItr = groups.begin(); while (gItr != groups.end()) { if (gItr->first.account == account) { - std::map::const_iterator lItr = gItr; + std::map::const_iterator lItr = gItr; ++gItr; groups.erase(lItr); } else { diff --git a/ui/models/roster.h b/ui/models/roster.h index b49cb1e..9471506 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -10,6 +10,7 @@ #include "item.h" #include "account.h" #include "contact.h" +#include "group.h" namespace Models { @@ -50,7 +51,7 @@ public: private: Item* root; std::map accounts; - std::map groups; + std::map groups; std::multimap contacts; private slots: diff --git a/ui/newcontact.ui b/ui/newcontact.ui index 923397d..4a08801 100644 --- a/ui/newcontact.ui +++ b/ui/newcontact.ui @@ -7,7 +7,7 @@ 0 0 376 - 222 + 208 diff --git a/ui/squawk.cpp b/ui/squawk.cpp index e8dae81..5cc28f6 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -12,8 +12,6 @@ Squawk::Squawk(QWidget *parent) : contextMenu(new QMenu()), dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()) { - setWindowIcon(QIcon(":images/logo.svg")); - m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu);