Better way to store expanded elements in roster, several clean ups, translations

This commit is contained in:
Blue 2022-08-17 19:25:35 +03:00
parent 7e9eed2075
commit d162494ec8
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
18 changed files with 874 additions and 252 deletions

View File

@ -3,9 +3,12 @@
## Squawk 0.2.3 (UNRELEASED)
### Bug fixes
- "Add contact" and "Join conference" menu are enabled once again (pavavno)!
- availability is now read from the same section of config file it was stored
### Improvements
- deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues
- all of the expandable roster items now get saved between launches
- settings file on the disk is not rewritten every roster element expansion or collapse
### New features
- Now you can enable tray icon from settings!

View File

@ -30,16 +30,18 @@ Application::Application(Core::Squawk* p_core):
storage(),
trayIcon(nullptr),
actionQuit(Shared::icon("application-exit"), tr("Quit")),
actionToggle(tr("Minimize to tray"))
actionToggle(QApplication::windowIcon(), tr("Minimize to tray")),
expandedPaths()
{
connect(&actionQuit, &QAction::triggered, this, &Application::quit);
connect(&actionToggle, &QAction::triggered, this, &Application::toggleSquawk);
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
connect(&roster, &Models::Roster::addedElement, this, &Application::onAddedElement);
//connecting myself to the backed
//connecting myself to the backend
connect(this, &Application::changeState, core, &Core::Squawk::changeState);
connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined);
connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin);
@ -70,7 +72,7 @@ Application::Application(Core::Squawk* p_core):
connect(core, &Core::Squawk::changeAccount, this, &Application::changeAccount);
connect(core, &Core::Squawk::removeAccount, this, &Application::removeAccount);
connect(core, &Core::Squawk::addContact, this, &Application::addContact);
connect(core, &Core::Squawk::addContact, &roster, &Models::Roster::addContact);
connect(core, &Core::Squawk::addGroup, this, &Application::addGroup);
connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup);
connect(core, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
@ -168,6 +170,8 @@ void Application::createMainWindow()
connect(squawk, &Squawk::openConversation, this, &Application::openConversation);
connect(squawk, &Squawk::changeState, this, &Application::setState);
connect(squawk, &Squawk::changeTray, this, &Application::onChangeTray);
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed);
connect(squawk, &Squawk::quit, this, &Application::quit);
connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing);
@ -192,7 +196,19 @@ void Application::createMainWindow()
dialogueQueue.setParentWidnow(squawk);
squawk->stateChanged(availability);
squawk->raise();
squawk->show();
squawk->activateWindow();
for (const std::list<QString>& entry : expandedPaths) {
QModelIndex ind = roster.getIndexByPath(entry);
if (ind.isValid()) {
squawk->expand(ind);
}
}
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed);
}
}
@ -292,6 +308,32 @@ void Application::toggleSquawk()
}
}
void Application::onItemCollapsed(const QModelIndex& index)
{
std::list<QString> address = roster.getItemPath(index);
if (address.size() > 0) {
expandedPaths.erase(address);
}
}
void Application::onItemExpanded(const QModelIndex& index)
{
std::list<QString> address = roster.getItemPath(index);
if (address.size() > 0) {
expandedPaths.insert(address);
}
}
void Application::onAddedElement(const std::list<QString>& path)
{
if (squawk != nullptr && expandedPaths.count(path) > 0) {
QModelIndex index = roster.getIndexByPath(path);
if (index.isValid()) {
squawk->expand(index);
}
}
}
void Application::notify(const QString& account, const Shared::Message& msg)
{
QString jid = msg.getPenPalJid();
@ -427,6 +469,18 @@ void Application::readSettings()
} else {
avail = static_cast<int>(Shared::Availability::online);
}
settings.beginGroup("roster");
QStringList entries = settings.allKeys();
for (const QString& entry : entries) {
QStringList p = entry.split("/");
if (p.last() == "expanded" && settings.value(entry, false).toBool()) {
p.pop_back();
expandedPaths.emplace(p.begin(), p.end());
}
}
settings.endGroup();
settings.endGroup();
setState(Shared::Global::fromInt<Shared::Availability>(avail));
@ -440,7 +494,22 @@ void Application::readSettings()
void Application::writeSettings()
{
QSettings settings;
settings.beginGroup("ui");
settings.setValue("availability", static_cast<int>(availability));
settings.remove("roster");
settings.beginGroup("roster");
for (const std::list<QString>& address : expandedPaths) {
QString path = "";
for (const QString& hop : address) {
path += hop + "/";
}
path += "expanded";
settings.setValue(path, true);
}
settings.endGroup();
settings.endGroup();
}
void Application::requestPassword(const QString& account, bool authenticationError) {
@ -611,25 +680,6 @@ void Application::changeAccount(const QString& account, const QMap<QString, QVar
}
}
void Application::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
{
roster.addContact(account, jid, group, data);
if (squawk != nullptr) {
QSettings settings;
settings.beginGroup("ui");
settings.beginGroup("roster");
settings.beginGroup(account);
if (settings.value("expanded", false).toBool()) {
QModelIndex ind = roster.getAccountIndex(account);
squawk->expand(ind);
}
settings.endGroup();
settings.endGroup();
settings.endGroup();
}
}
void Application::addGroup(const QString& account, const QString& name)
{
roster.addGroup(account, name);

View File

@ -18,6 +18,8 @@
#define APPLICATION_H
#include <map>
#include <list>
#include <set>
#include <QObject>
#include <QDBusInterface>
@ -76,7 +78,6 @@ protected slots:
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
void addGroup(const QString& account, const QString& name);
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
void requestPassword(const QString& account, bool authenticationError);
@ -97,6 +98,9 @@ private slots:
void onChangeTray(bool enabled, bool hide);
void trayClicked(QSystemTrayIcon::ActivationReason reason);
void toggleSquawk();
void onItemExpanded(const QModelIndex& index);
void onItemCollapsed(const QModelIndex& index);
void onAddedElement(const std::list<QString>& path);
private:
void createMainWindow();
@ -122,6 +126,7 @@ private:
QSystemTrayIcon* trayIcon;
QAction actionQuit;
QAction actionToggle;
std::set<std::list<QString>> expandedPaths;
};
#endif // APPLICATION_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -75,6 +75,11 @@ void Models::Element::update(const QString& field, const QVariant& value)
}
}
QString Models::Element::getId() const
{
return jid;
}
QString Models::Element::getAvatarPath() const
{
return avatarPath;

View File

@ -38,6 +38,7 @@ public:
QString getAvatarPath() const;
virtual void update(const QString& field, const QVariant& value);
virtual QString getId() const override;
void addMessage(const Shared::Message& data);
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);

View File

@ -181,6 +181,11 @@ QString Models::Item::getName() const
return name;
}
QString Models::Item::getId() const
{
return name;
}
QVariant Models::Item::data(int column) const
{
if (column != 0) {

View File

@ -65,6 +65,7 @@ class Item : public QObject{
virtual void appendChild(Item *child);
virtual void removeChild(int index);
virtual QString getDisplayedName() const;
virtual QString getId() const;
QString getName() const;
void setName(const QString& name);

View File

@ -75,6 +75,11 @@ QString Models::Reference::getDisplayedName() const
return original->getDisplayedName();
}
QString Models::Reference::getId() const
{
return original->getId();
}
Models::Item * Models::Reference::dereference()
{
return original;

View File

@ -38,6 +38,8 @@ public:
QString getDisplayedName() const override;
void appendChild(Models::Item * child) override;
void removeChild(int index) override;
QString getId() const override;
Item* dereference();
const Item* dereferenceConst() const;

View File

@ -76,7 +76,6 @@ private:
private:
bool autoJoin;
bool joined;
QString jid;
QString nick;
QString subject;
std::map<QString, Participant*> participants;

View File

@ -52,6 +52,8 @@ void Models::Roster::addAccount(const QMap<QString, QVariant>& data)
root->appendChild(acc);
accounts.insert(std::make_pair(acc->getName(), acc));
accountsModel->addAccount(acc);
emit addedElement({acc->getId()});
}
QVariant Models::Roster::data (const QModelIndex& index, int role) const
@ -433,6 +435,9 @@ void Models::Roster::addGroup(const QString& account, const QString& name)
Group* group = new Group({{"name", name}});
groups.insert(std::make_pair(id, group));
acc->appendChild(group);
emit addedElement({acc->getId(), group->getId()});
} else {
qDebug() << "An attempt to add group " << name << " to non existing account " << account << ", skipping";
}
@ -470,6 +475,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
}
}
std::list<QString> path = {acc->getId()};
if (group == "") {
if (acc->getContact(jid) != -1) {
qDebug() << "An attempt to add a contact" << jid << "to the ungrouped contact set of account" << account << "for the second time, skipping";
@ -486,6 +492,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
}
parent = itr->second;
path.push_back(parent->getId());
if (parent->getContact(jid) != -1) {
qDebug() << "An attempt to add a contact" << jid << "to the group" << group << "for the second time, skipping";
@ -502,11 +509,14 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
}
}
path.push_back(contact->getId());
if (ref == 0) {
ref = new Reference(contact);
}
parent->appendChild(ref);
emit addedElement(path);
}
void Models::Roster::removeGroup(const QString& account, const QString& name)
@ -694,6 +704,7 @@ void Models::Roster::addPresence(const QString& account, const QString& jid, con
if (itr != contacts.end()) {
itr->second->addPresence(name, data);
}
}
void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name)
@ -809,6 +820,8 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM
connect(room, &Room::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
rooms.insert(std::make_pair(id, room));
acc->appendChild(room);
emit addedElement({acc->getId(), room->getId()});
}
void Models::Roster::changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data)
@ -961,7 +974,7 @@ bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, co
}
}
QModelIndex Models::Roster::getAccountIndex(const QString& name)
QModelIndex Models::Roster::getAccountIndex(const QString& name) const
{
std::map<QString, Account*>::const_iterator itr = accounts.find(name);
if (itr == accounts.end()) {
@ -971,7 +984,7 @@ QModelIndex Models::Roster::getAccountIndex(const QString& name)
}
}
QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& name)
QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& name) const
{
std::map<QString, Account*>::const_iterator itr = accounts.find(account);
if (itr == accounts.end()) {
@ -987,7 +1000,7 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
}
}
QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource)
QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource) const
{
std::map<QString, Account*>::const_iterator itr = accounts.find(account);
if (itr == accounts.end()) {
@ -1113,3 +1126,66 @@ void Models::Roster::recalculateUnreadMessages()
}
emit unreadMessagesCountChanged(count);
}
std::list<QString> Models::Roster::getItemPath(const QModelIndex& index) const
{
std::list<QString> result;
if (index.isValid() && index.model() == this) {
Item* item = static_cast<Item*>(index.internalPointer());
while (item->type != Item::root) {
result.push_front(item->getId());
item = item->parentItem();
}
}
return result;
}
QModelIndex Models::Roster::getIndexByPath(const std::list<QString>& path) const
{
if (path.empty())
return QModelIndex();
QModelIndex current;
for (const QString& hop : path) {
int rows = rowCount(current);
bool found = false;
for (int i = 0; i < rows; ++i) {
QModelIndex el = index(i, 0, current);
Item* item = static_cast<Item*>(el.internalPointer());
if (item->getId() == hop) {
found = true;
current = el;
break;
}
}
if (!found)
break;
}
return current; //this way I will return the last matching model index, may be it's logically incorrect
// std::list<QString>::const_iterator pathItr = path.begin();
// QString accName = *pathItr;
// QModelIndex accIndex = getAccountIndex(accName);
// if (path.size() == 1)
// return accIndex;
//
// if (!accIndex.isValid())
// return QModelIndex();
//
// ++pathItr;
// ElId id{accName, *pathItr};
// QModelIndex contactIndex = getContactIndex(id.account, id.name);
// if (!contactIndex.isValid())
// contactIndex = getGroupIndex(id.account, id.name);
//
// if (path.size() == 2)
// return contactIndex;
//
// if (!contactIndex.isValid())
// return QModelIndex();
//
// ++pathItr;
}

View File

@ -22,6 +22,7 @@
#include <qabstractitemmodel.h>
#include <deque>
#include <map>
#include <list>
#include <QVector>
#include "shared/message.h"
@ -86,9 +87,12 @@ public:
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource) const;
Account* getAccount(const QString& name);
const Account* getAccountConst(const QString& name) const;
QModelIndex getAccountIndex(const QString& name);
QModelIndex getGroupIndex(const QString& account, const QString& name);
QModelIndex getContactIndex(const QString& account, const QString& jid, const QString& resource = "");
QModelIndex getAccountIndex(const QString& name) const;
QModelIndex getGroupIndex(const QString& account, const QString& name) const;
QModelIndex getContactIndex(const QString& account, const QString& jid, const QString& resource = "") const;
QModelIndex getIndexByPath(const std::list<QString>& path) const;
std::list<QString> getItemPath(const QModelIndex& index) const;
bool markMessageAsRead(const ElId& elementId, const QString& messageId);
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
@ -104,6 +108,7 @@ signals:
void unreadMessagesCountChanged(int count);
void unnoticedMessage(const QString& account, const Shared::Message& msg);
void localPathInvalid(const QString& path);
void addedElement(const std::list<QString>& path); //emits only on addition of Account, Contact, Room or Group. Presence and Participant are ignored
private slots:
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);

View File

@ -60,7 +60,8 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) :
connect(m_ui->comboBox, qOverload<int>(&QComboBox::activated), this, &Squawk::onComboboxActivated);
//connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked);
connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu);
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::itemCollapsed);
connect(m_ui->roster, &QTreeView::expanded, this, &Squawk::itemExpanded);
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
connect(rosterModel.accountsModel, &Models::Accounts::changed, this, &Squawk::onAccountsChanged);
@ -491,51 +492,9 @@ void Squawk::writeSettings()
settings.endGroup();
settings.setValue("splitter", m_ui->splitter->saveState());
settings.remove("roster");
settings.beginGroup("roster");
int size = rosterModel.accountsModel->rowCount(QModelIndex());
for (int i = 0; i < size; ++i) {
QModelIndex acc = rosterModel.index(i, 0, QModelIndex());
Models::Account* account = rosterModel.accountsModel->getAccount(i);
QString accName = account->getName();
settings.beginGroup(accName);
settings.setValue("expanded", m_ui->roster->isExpanded(acc));
std::deque<QString> groups = rosterModel.groupList(accName);
for (const QString& groupName : groups) {
settings.beginGroup(groupName);
QModelIndex gIndex = rosterModel.getGroupIndex(accName, groupName);
settings.setValue("expanded", m_ui->roster->isExpanded(gIndex));
settings.endGroup();
}
settings.endGroup();
}
settings.endGroup();
settings.endGroup();
settings.sync();
}
void Squawk::onItemCollepsed(const QModelIndex& index)
{
QSettings settings;
Models::Item* item = static_cast<Models::Item*>(index.internalPointer());
switch (item->type) {
case Models::Item::account:
settings.setValue("ui/roster/" + item->getName() + "/expanded", false);
break;
case Models::Item::group: {
QModelIndex accInd = rosterModel.parent(index);
Models::Account* account = rosterModel.accountsModel->getAccount(accInd.row());
settings.setValue("ui/roster/" + account->getName() + "/" + item->getName() + "/expanded", false);
}
break;
default:
break;
}
}
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
{
if (restoreSelection.isValid() && restoreSelection == current) {

View File

@ -83,6 +83,8 @@ signals:
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
void itemExpanded (const QModelIndex& index);
void itemCollapsed (const QModelIndex& index);
public:
Models::Roster::ElId currentConversationId() const;
@ -127,7 +129,6 @@ private slots:
void onComboboxActivated(int index);
void onRosterItemDoubleClicked(const QModelIndex& item);
void onRosterContextMenu(const QPoint& point);
void onItemCollepsed(const QModelIndex& index);
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
void onContextAboutToHide();
void onAboutSquawkCalled();

View File

@ -100,7 +100,7 @@ void PageGeneral::onTrayChecked(int state)
emit variableModified("tray", enabled);
m_ui->hideTrayInput->setEnabled(enabled);
if (!enabled) {
m_ui->hideTrayInput->setEnabled(false);
m_ui->hideTrayInput->setChecked(false);
}
}